docs: add Pezkuwi-SDK documentation
- Technical documentation - SDK guides - Architecture overview - Whitepaper - Contributor guides
@@ -0,0 +1,22 @@
|
|||||||
|
# Audit
|
||||||
|
|
||||||
|
Audits are conducted to ensure the absence of severe or exploitable bugs. Pull Requests are generally merged into the
|
||||||
|
`master` branch without audit. The `audited` tag is used to track the latest audited commit of the `master` branch. This
|
||||||
|
means that audits need to happen in order of being merged.
|
||||||
|
This is an optimistic approach that lets us develop with greater speed, while requiring (possibly) large refactors in
|
||||||
|
the failure case.
|
||||||
|
|
||||||
|
Audits can be deferred if the logic is gated by an `experimental` feature or marked as "Not Production Ready" within the
|
||||||
|
first line of doc. Such changes should be queued manually before these warnings are removed.
|
||||||
|
|
||||||
|
## General Guidelines for what to Audit
|
||||||
|
|
||||||
|
There is no single one-fits-all rule. Generally we should audit important logic that could immediately be used on
|
||||||
|
production networks. If in doubt, ask in chat or in the Merge Request.
|
||||||
|
|
||||||
|
## Requesting an Audit
|
||||||
|
|
||||||
|
1. Add the PR to the project `Security Audit (PRs) - SRLabs`
|
||||||
|
2. Set status to Backlog
|
||||||
|
3. Assign priority, considering the universe of PRs currently in the backlog
|
||||||
|
4. Add the component
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Backporting
|
||||||
|
|
||||||
|
This document explains how to backport a merged PR from `master` to one of the `stable*` branches.
|
||||||
|
Backports should only be used to fix bugs or security issues - never to introduce new features.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Fix a bug through a PR that targets `master`.
|
||||||
|
2. Add label related to the branch to wich to backport changes to the PR.
|
||||||
|
- `A4-backport-stable2409`
|
||||||
|
- `A4-backport-stable2412`
|
||||||
|
- `A4-backport-stable2503`
|
||||||
|
- `A4-backport-stable2506`
|
||||||
|
3. Merge the PR into `master`.
|
||||||
|
4. Wait for the bot to open the backport PR.
|
||||||
|
5. Ensure the change is audited or does not need audit.
|
||||||
|
6. Merge the backport PR.(ℹ️ for the branches starting from 2412 it can be done automatically
|
||||||
|
if backport PR has reviews and the pipeline is green)
|
||||||
|
|
||||||
|
The label can also be added after the PR is merged.
|
||||||
|
|
||||||
|
ℹ️ If, for some reasons, a backport PR was not created automatically, it can be done manually.
|
||||||
|
But it is important, that the PR title follows the following pattern:
|
||||||
|
`[stableBranchName] Backport #originalPRNumber` (i.e. **[stable2412] Backport #8198**)
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
For example here where the dev triggered the process by adding the label after merging:
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
# Release
|
||||||
|
|
||||||
|
The outputs of a stable release are:
|
||||||
|
|
||||||
|
- The binaries:
|
||||||
|
- `pezkuwi`
|
||||||
|
- `pezkuwi-execute-worker`
|
||||||
|
- `pezkuwi-prepare-worker`
|
||||||
|
- `pezkuwi-teyrchain`
|
||||||
|
- `pezkuwi-omni-node`
|
||||||
|
- `chain-spec-builder`
|
||||||
|
- `frame-omni-bencher`
|
||||||
|
|
||||||
|
built for the `x86_64-unknown-linux-gnu` and `aarch64-apple-darwin` targets. The gpg signatures and sha256 checksums.
|
||||||
|
|
||||||
|
- The runtimes for Zagros and its system teyrchains.
|
||||||
|
- The new crate versions published to `crates.io`.
|
||||||
|
- Debian package for the Pezkuwi binary.
|
||||||
|
- Docker images for:
|
||||||
|
- `pezkuwi` (includes `pezkuwi-execute-worker` & `pezkuwi-prepare-worker`)
|
||||||
|
- `pezkuwi-teyrchain`
|
||||||
|
- `pezkuwi-omni-node`
|
||||||
|
- `chain-spec-builder`
|
||||||
|
|
||||||
|
|
||||||
|
# Timeline
|
||||||
|
`Stable` releases are scheduled on a quarterly basis, usually by the end of the last month of each quarter. The exact
|
||||||
|
schedule can be found on the [Release Registry](https://github.com/paritytech/release-registry/).It is possible to
|
||||||
|
subscribe to a [calendar link](https://raw.githubusercontent.com/paritytech/release-registry/main/releases-v1.ics)
|
||||||
|
to have it in your personal calendar.
|
||||||
|
|
||||||
|
Each stable release is supported for a period of one year from its first release. For example, `Pezkuwi stable2412`
|
||||||
|
was released on `2024-12-17` and its end of life is set to `2025-12-16`.
|
||||||
|
|
||||||
|
During this period, each stable release is updated with patch releases, which are scheduled on a monthly basis
|
||||||
|
and contain fixes for any bugs that may be found.
|
||||||
|
|
||||||
|
ℹ️ Note: the binaries and runtimes (if needed) are only provided for the latest `stable` release, for the previous
|
||||||
|
releases only the crates.io release takes place.
|
||||||
|
|
||||||
|
This three month period between `stable` releases includes a 1.5 month QA period. This means that for each upcoming
|
||||||
|
`stable` release, the branch from which that release will be made is created 1.5 months before the release date.
|
||||||
|
This time is used to test the upcoming release candidate and find any issues that may arise with publishing crates
|
||||||
|
to crates.io, binary artifacts and template synchronisation before the final release. The findings should be fixed
|
||||||
|
and backported to the release branch. During this time, multiple release candidates may be built and rolled out.
|
||||||
|
|
||||||
|
# Setup
|
||||||
|
|
||||||
|
We have two types of branches related to the releases: `master` and `stableYYMM`:
|
||||||
|
- `master` is the main development branch where normal Pull Requests are opened. Developers need to mostly only care
|
||||||
|
about this branch.
|
||||||
|
- `stableYYMM` branch contains a version of the code that is ready to be released. Each `stableYYMM` branch corresponds
|
||||||
|
to the corresponding stable release, which is in the maintenance or in a QA period. Its contents should be always
|
||||||
|
audited. Merging to it is restricted to [Backports](#backports).
|
||||||
|
|
||||||
|
# Versioning
|
||||||
|
|
||||||
|
We are releasing multiple different things from this repository in one release, but we don't want to use the same
|
||||||
|
version for everything. Thus, in the following we explain the versioning story for the crates, node and Zagros.
|
||||||
|
|
||||||
|
To easily refer to a release, it shall be named by its date in the form `Pezkuwi stableYYMM`. Patches to stable releases
|
||||||
|
are tagged in the form of `Pezkuwi stableYYMM-PATCH`, with `PATCH` ranging from 1 to 99. For example, the fourth patch
|
||||||
|
to `Pezkuwi stable2409` would be `Pezkuwi stable2409-4`.
|
||||||
|
|
||||||
|
## Crate
|
||||||
|
|
||||||
|
We try to follow [SemVer 2.0.0](https://semver.org/) as best as possible for versioning our crates. The definitions of
|
||||||
|
`major`, `minor` and `patch` version for Rust crates are slightly altered from their standard for pre `1.0.0` versions.
|
||||||
|
Quoting [rust-lang.org](https://doc.rust-lang.org/cargo/reference/semver.html):
|
||||||
|
|
||||||
|
>Initial development releases starting with "0.y.z" can treat changes in "y" as a major release, and "z" as a minor
|
||||||
|
release. "0.0.z" releases are always major changes. This is because Cargo uses the convention that only changes in the
|
||||||
|
left-most non-zero component are considered incompatible.
|
||||||
|
|
||||||
|
SemVer requires a piece of software to first declare a public API. The public API of the Pezkuwi SDK
|
||||||
|
is hereby declared as the sum of all crates' public APIs.
|
||||||
|
|
||||||
|
Inductively, the public API of our library crates is declared as all public items that are neither:
|
||||||
|
- Inside a `__private` module
|
||||||
|
- Documented as "unstable" or "experimental" in the first line of docs
|
||||||
|
- Bear `unstable` or `experimental` in their absolute path
|
||||||
|
|
||||||
|
## Node
|
||||||
|
|
||||||
|
The versioning of the Pezkuwi node is done most of the time by only incrementing the `minor` version. The `major`
|
||||||
|
version is only bumped for special releases and the `patch` is used for a patch release that happens every month and
|
||||||
|
fixes found issues. The node version is not following SemVer. This means that the version doesn't express if there are
|
||||||
|
any breaking changes in the CLI interface or similar. The node version is declared
|
||||||
|
in the [`NODE_VERSION`](https://docs.pezkuwichain.io/sdk/master/polkadot_node_primitives/constant.NODE_VERSION.html)
|
||||||
|
variable.
|
||||||
|
|
||||||
|
## Zagros
|
||||||
|
|
||||||
|
For the Westene testnet, in addition to incrementing the `Cargo.toml` version we also increment the `spec_version` and
|
||||||
|
sometimes the `transaction_version`. The spec version is also following the node version. Its schema is: `M_mmm_ppp` and
|
||||||
|
for example `1_002_000` is the node release `1.2.0`. This versioning has no further meaning, and is only done to map
|
||||||
|
from an on chain `spec_version` easily to the release in this repository.
|
||||||
|
|
||||||
|
|
||||||
|
# Backports
|
||||||
|
|
||||||
|
**From `master` to `stable`**
|
||||||
|
|
||||||
|
Backports in this direction can be anything that is audited and either a `minor` or a `patch` bump.
|
||||||
|
See [BACKPORT.md](./BACKPORT.md) for more explanation. [Security fixes](#bug-and-security-fix)
|
||||||
|
should be prioritized over additions or improvements. Crates that are declared as internal API can
|
||||||
|
also have `major` version bumps through backports.
|
||||||
|
|
||||||
|
**From `stable` to `master`**
|
||||||
|
|
||||||
|
Backports to `master` only happen after a `stable` or `patch` release has been made for the current stable release,
|
||||||
|
and include node version and spec version bumps, plus reorganizing the prdoc files
|
||||||
|
(they should go into the appropriate release folder under the [prdoc](./proc) folder).
|
||||||
|
This is done by the release team to keep things organized. Developers need not to care about such backports.
|
||||||
|
|
||||||
|
# Processes
|
||||||
|
|
||||||
|
The following processes are necessary to actualize our releases. Each process has a *Cadence* on which it must execute
|
||||||
|
and a *Responsible* that is responsible for autonomously doing so and reporting back any error in the *RelEng: Pezkuwi
|
||||||
|
Release Coordination* Matrix channel. All processes should be automated as much as possible.
|
||||||
|
|
||||||
|
## Crate Bumping
|
||||||
|
|
||||||
|
Cadence: currently every 3 months for new `stable` releases and monthly for existing `stables`.
|
||||||
|
Responsible: Developer that opened the Pull Request.
|
||||||
|
|
||||||
|
Following SemVer isn't easy, but there exists [a guide](https://doc.rust-lang.org/cargo/reference/semver.html) in the
|
||||||
|
Rust documentation that explains the small details on when to bump what. This process is supported with a CI check that
|
||||||
|
utilizes [`cargo-semver-checks`](https://github.com/obi1kenobi/cargo-semver-checks).
|
||||||
|
|
||||||
|
### Steps
|
||||||
|
|
||||||
|
1. Developer opens a Pull Request with changed crates against `master`.
|
||||||
|
2. They mention the type of the bump of all changed crates according to SemVer in the prdoc file attached to the PR.
|
||||||
|
Note that this includes any crates that expose the changed behaviour in their *public API* and also transitive dependencies
|
||||||
|
for whom the same rule applies.
|
||||||
|
3. The bump itself happens during the release and is done by a release engineer using the
|
||||||
|
[Parity-Publish](https://github.com/paritytech/parity-publish) tool.
|
||||||
|
|
||||||
|
## Stable Release
|
||||||
|
|
||||||
|
Cadence: every 3 months for new `stable` releases and monthly for existing `stables`. Responsible: Release Team.
|
||||||
|
|
||||||
|
### Steps to execute a new stable binary release
|
||||||
|
|
||||||
|
From the main Pezkuwi-sdk repository in the paritytech org:
|
||||||
|
|
||||||
|
1. On the cut-off date, create a new branch with the name `satbleYYMM`
|
||||||
|
using combined [Branch-off stable/tag rc flow](/.github/workflows/release-10_branchoff-stable.yml)
|
||||||
|
2. Create a new rc tag from the stable branch using combined
|
||||||
|
[Branch-off stable/tag rc flow](/.github/workflows/release-10_branchoff-stable.yml)
|
||||||
|
|
||||||
|
ℹ️ These first two steps can be done all in one if there are no extra actions (like crates release) are needed
|
||||||
|
to be done in between.
|
||||||
|
In case of a crates release: when it is done, the changes done by the Parity-Publish needs to be revereted and
|
||||||
|
merged back to the stable branch via a PR as the direct pushes are restricted. When this is done,
|
||||||
|
the new RC tag can be created using the flow from above.
|
||||||
|
|
||||||
|
From the forked Pezkuwi-sdk repository in the [paritytech-release org](https://github.com/paritytech-release/polkadot-sdk/actions):
|
||||||
|
|
||||||
|
1. Sync the forks before continuing with the release using
|
||||||
|
[Sync the forked repo with the upstream](https://github.com/paritytech-release/polkadot-sdk/actions/workflows/fork-sync-action.yml)
|
||||||
|
2. To build binaries or testnet runtimes and to create a release draft in the github
|
||||||
|
trigger [Release - Combined Builds Flow](/.github/workflows/release-22_combined-rc-runtime-builds-release-draft.yml)
|
||||||
|
3. Repeat steps 1 and 2 to prepare a new rc if there are any changes were pushed to release branch
|
||||||
|
4. When the release is finalized and ready to go, publish crates using `parity-publish` tool and push changes
|
||||||
|
to the release branch
|
||||||
|
5. Trigger [Release - Promote RC to final candidate on S3](/.github/workflows/release-31_promote-rc-to-final.yml)
|
||||||
|
to have it as a final rc on the S3
|
||||||
|
6. Publish deb package for the `pezkuwi` binary using
|
||||||
|
[Release - Publish Pezkuwi deb package](/.github/workflows/release-40_publish-deb-package.yml)
|
||||||
|
7. Adjust the release draft and publish release on GitHub.
|
||||||
|
8. Publish docker images using [Release - Publish Docker Image](/.github/workflows/release-50_publish-docker.yml)
|
||||||
|
|
||||||
|
From the main Pezkuwi-sdk repository in the paritytech org:
|
||||||
|
|
||||||
|
1. Synchronize templates using [Synchronize templates](/.github/workflows/misc-sync-templates.yml)
|
||||||
|
2. Update the [Release Registry](https://github.com/paritytech/release-registry/)
|
||||||
|
follwoing the [instructions](https://github.com/paritytech/release-registry?tab=readme-ov-file#maintenance)
|
||||||
|
in the repo with the actual release dates.
|
||||||
|
|
||||||
|
## Patch release for the latest stable version
|
||||||
|
|
||||||
|
Cadence: every month. Responsible: Developer
|
||||||
|
|
||||||
|
Backporting rules can be found in the [BACKPORT.md](/docs/BACKPORT.md)
|
||||||
|
|
||||||
|
The release itself is similar to [the new stable release](#steps-to-execute-a-new-stable-release) process without
|
||||||
|
the branching-off step, as the branch already exists and depending on the patch
|
||||||
|
(whether it is for the current `stable` release or one of the previous ones) the binary build can be skipped
|
||||||
|
and only crates and GitHub publishing is done.
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as contributors and maintainers
|
||||||
|
pledge to making participation in our project and our community a harassment-free experience for
|
||||||
|
everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level
|
||||||
|
of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic address, without explicit
|
||||||
|
permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
### Facilitation, Not Strongarming
|
||||||
|
|
||||||
|
We recognize that this software is merely a tool for users to create and maintain their blockchain
|
||||||
|
of preference. We see that blockchains are naturally community platforms with users being the
|
||||||
|
ultimate decision makers. We assert that good software will maximize user agency by facilitating
|
||||||
|
user-expression on the network. As such:
|
||||||
|
|
||||||
|
* This project will strive to give users as much choice as is both reasonable and possible over what
|
||||||
|
protocol they adhere to; but
|
||||||
|
* Use of the project's technical forums, commenting systems, pull requests and issue trackers as a
|
||||||
|
means to express individual protocol preferences is forbidden.
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable behavior and are
|
||||||
|
expected to take appropriate and fair corrective action in response to any instances of unacceptable
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits,
|
||||||
|
code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to
|
||||||
|
ban temporarily or permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces when an individual is
|
||||||
|
representing the project or its community. Examples of representing a project or community include
|
||||||
|
using an official project e-mail address, posting via an official social media account, or acting as
|
||||||
|
an appointed representative at an online or offline event. Representation of a project may be further
|
||||||
|
defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting
|
||||||
|
the project team at <admin@parity.io>. The project team will review and investigate all complaints,
|
||||||
|
and will respond in a way that it deems appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident. Further details of
|
||||||
|
specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary
|
||||||
|
or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at
|
||||||
|
https://contributor-covenant.org/version/1/4
|
||||||
|
|
||||||
|
[homepage]: https://contributor-covenant.org
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
The `Pezkuwi SDK` project is an **OPENISH Open Source Project**
|
||||||
|
|
||||||
|
## What?
|
||||||
|
|
||||||
|
Individuals making significant and valuable contributions are given commit-access to the project. Contributions are done
|
||||||
|
via pull-requests and need to be approved by the maintainers.
|
||||||
|
|
||||||
|
> **Note:** Contributors who are part of the organization do not need to fork the repository. They can create a branch
|
||||||
|
> directly in the repository to send a pull request.
|
||||||
|
|
||||||
|
## How?
|
||||||
|
|
||||||
|
In order to build this project you need to install some dependencies, follow the instructions in [this guide](https://docs.pezkuwichain.io/develop/parachains/install-polkadot-sdk).
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
There are a few basic ground-rules for contributors (including the maintainer(s) of the project):
|
||||||
|
|
||||||
|
1. **No `--force` pushes** or modifying the main branch history in any way. If you need to rebase, ensure you do it in
|
||||||
|
your own repo. No rewriting of the history after the code has been shared (e.g. through a Pull-Request).
|
||||||
|
2. **Feature branches** must follow the naming conventions below and be used for ongoing work.
|
||||||
|
3. **All modifications** must be made in a **pull-request** to solicit feedback from other contributors.
|
||||||
|
4. A pull-request **must not be merged until CI** has finished successfully.
|
||||||
|
5. Contributors should adhere to the [house coding style](./STYLE_GUIDE.md).
|
||||||
|
6. Contributors should adhere to the [house documenting style](./DOCUMENTATION_GUIDELINES.md), when applicable.
|
||||||
|
|
||||||
|
## Branch Naming Conventions
|
||||||
|
|
||||||
|
All branches must follow these naming patterns:
|
||||||
|
|
||||||
|
| Pattern | Purpose | Example |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `feature/<name>` | New features | `feature/parliamentary-nft-voting` |
|
||||||
|
| `fix/<name>` | Bug fixes | `fix/presale-overflow-check` |
|
||||||
|
| `pallet/<name>` | Pallet-specific changes | `pallet/welati-liquid-democracy` |
|
||||||
|
| `runtime/<name>` | Runtime configuration changes | `runtime/xcm-v5-upgrade` |
|
||||||
|
| `docs/<name>` | Documentation updates | `docs/token-economics` |
|
||||||
|
| `ci/<name>` | CI/CD changes | `ci/benchmark-workflow` |
|
||||||
|
| `refactor/<name>` | Code refactoring | `refactor/treasury-cleanup` |
|
||||||
|
| `test/<name>` | Test additions/fixes | `test/presale-edge-cases` |
|
||||||
|
| `hotfix/<name>` | Urgent production fixes | `hotfix/critical-security-patch` |
|
||||||
|
|
||||||
|
**Branch name rules:**
|
||||||
|
- Use lowercase letters, numbers, and hyphens only
|
||||||
|
- Keep names concise but descriptive
|
||||||
|
- No spaces or special characters
|
||||||
|
- Maximum 50 characters for branch name
|
||||||
|
|
||||||
|
**Protected branches:**
|
||||||
|
- `main` - Production branch, requires PR with review
|
||||||
|
- `staging` - Pre-production testing
|
||||||
|
- `develop` - Integration branch (if used)
|
||||||
|
|
||||||
|
## Merge Process
|
||||||
|
|
||||||
|
### In General
|
||||||
|
|
||||||
|
- A Pull Request (PR) needs to be reviewed and approved by project maintainers.
|
||||||
|
- If a change does not alter any logic (e.g. comments, dependencies, docs), then it may be tagged `A1-insubstantial` and
|
||||||
|
merged faster.
|
||||||
|
- No PR should be merged until all reviews' comments are addressed.
|
||||||
|
|
||||||
|
### Labels
|
||||||
|
|
||||||
|
The set of labels and their description can be found [here](https://docs.pezkuwichain.io/labels/doc_polkadot-sdk.html).
|
||||||
|
|
||||||
|
### Process
|
||||||
|
|
||||||
|
1. Please use our [Pull Request Template](./PULL_REQUEST_TEMPLATE.md) and make sure all relevant information is
|
||||||
|
reflected in your PR.
|
||||||
|
2. Please tag each PR with minimum one `T*` label. The respective `T*` labels should signal the component that was
|
||||||
|
changed, they are also used by downstream users to track changes and to include these changes properly into their own
|
||||||
|
releases.
|
||||||
|
3. If you’re still working on your PR, please submit as “Draft”. Once a PR is ready for review change the status to
|
||||||
|
“Open”, so that the maintainers get to review your PR. Generally PRs should sit for 48 hours in order to garner
|
||||||
|
feedback. It may be merged before if all relevant parties had a look at it.
|
||||||
|
4. With respect to auditing, please see [AUDIT.md](../AUDIT.md). In general, merging to master can happen independently of
|
||||||
|
audit.
|
||||||
|
5. PRs will be able to be merged once all reviewers' comments are addressed and CI is successful.
|
||||||
|
|
||||||
|
**Noting breaking changes:** When breaking APIs, the PR description should mention what was changed alongside some
|
||||||
|
examples on how to change the code to make it work/compile. It should also mention potential storage migrations and if
|
||||||
|
they require some special setup aside from adding it to the list of migrations in the runtime.
|
||||||
|
|
||||||
|
## Reviewing pull requests
|
||||||
|
|
||||||
|
When reviewing a pull request, the end-goal is to suggest useful changes to the author. Reviews should finish with
|
||||||
|
approval unless there are issues that would result in:
|
||||||
|
1. Buggy behavior.
|
||||||
|
2. Undue maintenance burden.
|
||||||
|
3. Breaking with house coding style.
|
||||||
|
4. Pessimization (i.e. reduction of speed as measured in the projects benchmarks).
|
||||||
|
5. Feature reduction (i.e. it removes some aspect of functionality that a significant minority of users rely on).
|
||||||
|
6. Uselessness (i.e. it does not strictly add a feature or fix a known issue).
|
||||||
|
|
||||||
|
The reviewers are also responsible to check:
|
||||||
|
|
||||||
|
- if the PR description is well written to facilitate integration, in case it contains breaking changes.
|
||||||
|
- the PR has an impact on docs.
|
||||||
|
|
||||||
|
**Reviews may not be used as an effective veto for a PR because**:
|
||||||
|
1. There exists a somewhat cleaner/better/faster way of accomplishing the same feature/fix.
|
||||||
|
2. It does not fit well with some other contributors' longer-term vision for the project.
|
||||||
|
|
||||||
|
## `PRDoc`
|
||||||
|
|
||||||
|
All Pull Requests must contain proper title & description, as described in [Pull Request
|
||||||
|
Template](./PULL_REQUEST_TEMPLATE.md). Moreover, all pull requests must have a proper `prdoc` file attached.
|
||||||
|
|
||||||
|
Pull Requests labelled with `R0-no-crate-publish-required` are exempt from prdoc documentation requirements.
|
||||||
|
|
||||||
|
See more about `prdoc` [here](./prdoc.md)
|
||||||
|
|
||||||
|
## Crate Configuration `Cargo.toml`
|
||||||
|
|
||||||
|
The Pezkuwi SDK uses many conventions when configuring a crate. Watch out for these things when you
|
||||||
|
are creating a new crate.
|
||||||
|
|
||||||
|
### Is the Crate chain-specific?
|
||||||
|
|
||||||
|
Chain-specific crates, for example
|
||||||
|
[`bp-bridge-hub-pezkuwichain`](https://github.com/pezkuwichain/pezkuwi-sdk/blob/4014b9bf2bf8f74862f63e7114e5c78009529be5/bridges/chains/chain-bridge-hub-rococo/Cargo.toml#L10-L11)
|
||||||
|
, should not be released as part of the Pezkuwi-SDK umbrella crate. We have a custom metadata
|
||||||
|
attribute that is picked up by the [generate-umbrella.py](../../scripts/generate-umbrella.py)
|
||||||
|
script, that should be applied to all chain-specific crates like such:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
# Other stuff...
|
||||||
|
|
||||||
|
[package.metadata.pezkuwi-sdk]
|
||||||
|
exclude-from-umbrella = true
|
||||||
|
|
||||||
|
# Other stuff...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Is the Crate a Test, Example or Fuzzer?
|
||||||
|
|
||||||
|
Test or example crates, like
|
||||||
|
[`pallet-example-task`](https://github.com/pezkuwichain/pezkuwi-sdk/blob/9b4acf27b869d7cbb07b03f0857763b8c8cc7566/substrate/frame/examples/tasks/Cargo.toml#L9)
|
||||||
|
, should not be released to crates.io. To ensure this, you must add `publish = false` to your
|
||||||
|
crate's `package` section:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
# Other stuff...
|
||||||
|
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
# Other stuff...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helping out
|
||||||
|
|
||||||
|
We use [labels](https://github.com/pezkuwichain/pezkuwi-sdk/labels) to manage PRs and issues and communicate state of a
|
||||||
|
PR. Please familiarise yourself with them. Best way to get started is to a pick a ticket tagged
|
||||||
|
[easy](https://github.com/pezkuwichain/pezkuwi-sdk/issues?q=is%3Aopen+is%3Aissue+label%3AD0-easy) or
|
||||||
|
[medium](https://github.com/pezkuwichain/pezkuwi-sdk/issues?q=is%3Aopen+is%3Aissue+label%3AD1-medium) and get going.
|
||||||
|
Alternatively, look out for issues tagged
|
||||||
|
[mentor](https://github.com/pezkuwichain/pezkuwi-sdk/issues?q=is%3Aopen+is%3Aissue+label%3AC1-mentor) and get in contact
|
||||||
|
with the mentor offering their support on that larger task.
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
### Issues
|
||||||
|
|
||||||
|
If what you are looking for is an answer rather than proposing a new feature or fix, search
|
||||||
|
[https://exchange.pezkuwichain.app](https://exchange.pezkuwichain.app/) to see if an post already exists, and ask if
|
||||||
|
not. Please do not file support issues here.
|
||||||
|
|
||||||
|
Before opening a new issue search to see if a similar one already exists and leave a comment that you also experienced
|
||||||
|
this issue or add your specifics that are related to an existing issue.
|
||||||
|
|
||||||
|
Please label issues with the following labels (only relevant for maintainer):
|
||||||
|
1. `I*` issue severity and type. EXACTLY ONE REQUIRED.
|
||||||
|
2. `D*` issue difficulty, suggesting the level of complexity this issue has. AT MOST ONE ALLOWED.
|
||||||
|
3. `T*` Issue topic. MULTIPLE ALLOWED.
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
Declaring formal releases remains the prerogative of the project maintainer(s). See [RELEASE.md](../RELEASE.md).
|
||||||
|
|
||||||
|
## UI tests
|
||||||
|
|
||||||
|
UI tests are used for macros to ensure that the output of a macro doesn’t change and is in the expected format. These UI
|
||||||
|
tests are sensible to any changes in the macro generated code or to switching the rust stable version. The tests are
|
||||||
|
only run when the `RUN_UI_TESTS` environment variable is set. So, when the CI is for example complaining about failing
|
||||||
|
UI tests and it is expected that they fail these tests need to be executed locally. To simplify the updating of the UI
|
||||||
|
test output there is a script
|
||||||
|
- `./scripts/update-ui-tests.sh` to update the tests for a current rust version locally
|
||||||
|
- `./scripts/update-ui-tests.sh 1.70` # to update the tests for a specific rust version locally
|
||||||
|
|
||||||
|
Or if you have opened PR and you're member of `paritytech` - you can use [/cmd](./commands-readme.md)
|
||||||
|
to run the tests for you in CI:
|
||||||
|
- `/cmd update-ui` - will run the tests for the current rust version
|
||||||
|
- `/cmd update-ui --image docker.io/paritytech/ci-unified:bullseye-1.70.0-2023-05-23` -
|
||||||
|
will run the tests for the specified rust version and specified image
|
||||||
|
|
||||||
|
## Feature Propagation
|
||||||
|
|
||||||
|
We use [zepter](https://github.com/ggwpez/zepter) to enforce features are propagated between crates correctly.
|
||||||
|
|
||||||
|
## Command Bot
|
||||||
|
|
||||||
|
If you're member of **paritytech** org - you can use command-bot to run various of common commands in CI:
|
||||||
|
|
||||||
|
Start with comment in PR: `/cmd --help` to see the list of available commands.
|
||||||
|
|
||||||
|
## Debug builds
|
||||||
|
|
||||||
|
In order to improve build times for debug builds, the workspace `Cargo.toml` is configured to emit
|
||||||
|
source line debug information only. If you need full debug info in your local debug builds,
|
||||||
|
search for the line `debug = "line-tables-only"` and comment it out.
|
||||||
|
|
||||||
|
## Deprecating code
|
||||||
|
|
||||||
|
When deprecating and removing code you need to be mindful of how this could impact downstream developers. In order to
|
||||||
|
mitigate this impact, it is recommended to adhere to the steps outlined in the [Deprecation
|
||||||
|
Checklist](./DEPRECATION_CHECKLIST.md).
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Deprecation Checklist
|
||||||
|
|
||||||
|
Pezkuwi SDK is under constant development and improvement, thus deprecation and removal of existing code happen often.
|
||||||
|
When creating a breaking change we need to be mindful that external builders could be impacted by this.
|
||||||
|
The deprecation checklist tries to mitigate this impact, while still keeping the developer experience, the DevEx, as
|
||||||
|
smooth as possible.
|
||||||
|
|
||||||
|
To start a deprecation process, a new issue with the label `T15-deprecation` needs to be created for correct tracking.
|
||||||
|
Then these are the actions to take:
|
||||||
|
|
||||||
|
## Hard deprecate by adding a warning message
|
||||||
|
|
||||||
|
The warning message shall include a removal month and year, which is suggested to be 6 months after the deprecation
|
||||||
|
notice is released.
|
||||||
|
This means that the code will be removed in a release within that month (or after, but never before). Please use this
|
||||||
|
template, doing so makes it easy to search through the code base:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[deprecated(note = "[DEPRECATED] will be removed after [DATE]. [ALTERNATIVE]")]
|
||||||
|
```
|
||||||
|
`[ALTERNATIVE]` won't always be possible but offer it if it is.
|
||||||
|
|
||||||
|
E.g.
|
||||||
|
```rust
|
||||||
|
#[deprecated(note = "`GenesisConfig` will be removed after December 2023. Use `RuntimeGenesisConfig` instead.")]
|
||||||
|
```
|
||||||
|
|
||||||
|
Some pieces of code cannot be labeled as deprecated, like [reexports](https://github.com/rust-lang/rust/issues/30827)
|
||||||
|
or [dispatchables](https://github.com/pezkuwichain/pezkuwi-sdk/issues/100#issuecomment-1691684159), for instance.
|
||||||
|
In cases like that we can only make a visible enough comment, and make sure that we [announce the deprecation properly](#announce-the-deprecation-and-removal).
|
||||||
|
|
||||||
|
## Remove usage of the deprecated code in the code base
|
||||||
|
|
||||||
|
Just make sure that we are not using the deprecated code ourselves.
|
||||||
|
If you added the deprecation warning from the previous step, this can be done by making sure that warning is not shown
|
||||||
|
when building the code.
|
||||||
|
|
||||||
|
## Update examples and tutorials
|
||||||
|
|
||||||
|
Make sure that the rust docs are updated.
|
||||||
|
We also need [https://docs.pezkuwichain.io/](https://docs.pezkuwichain.io/) to be updated accordingly. The repo behind it is
|
||||||
|
[https://github.com/polkadot-developers/polkadot-docs](https://github.com/polkadot-developers/polkadot-docs).
|
||||||
|
|
||||||
|
## Announce the deprecation and removal
|
||||||
|
|
||||||
|
**At minimum they should be noted in the release log.** Please see how to document a PR [here](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CONTRIBUTING.md#documentation).
|
||||||
|
There you can give instructions based on the audience and tell them what they need to do to upgrade the code.
|
||||||
|
|
||||||
|
Some breaking changes have a bigger impact than others. When the impact is big the release note is not enough, though
|
||||||
|
it should still be the primary place for the notice. You can link back to the changelog files in other channels if you
|
||||||
|
want to announce it somewhere else.
|
||||||
|
Make sure you are as loud as you need to be for the magnitude of the breaking change.
|
||||||
|
|
||||||
|
## Removal version is planned
|
||||||
|
|
||||||
|
Depending on the removal date indicated in the deprecation warning in the [first step](#hard-deprecate-by-adding-a-warning-message),
|
||||||
|
the nature and the importance of the change, it might make sense to coordinate the release with other developers and
|
||||||
|
with the Release team.
|
||||||
|
|
||||||
|
## Deprecated code is removed
|
||||||
|
|
||||||
|
The deprecated code finally gets removed.
|
||||||
|
Don’t forget to [announce this accordingly](#announce-the-deprecation-and-removal).
|
||||||
|
|
||||||
|
✅ In order to not forget any of these steps, consider using this template in your deprecation issue:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- [ ] Deprecate code by adding a warning message
|
||||||
|
- [ ] Remove usage of the deprecated code in the code base
|
||||||
|
- [ ] Update examples and tutorials
|
||||||
|
- [ ] Announce code deprecation
|
||||||
|
- [ ] Plan removal version
|
||||||
|
- [ ] Announce code removal
|
||||||
|
- [ ] Remove deprecated code
|
||||||
|
```
|
||||||
@@ -0,0 +1,354 @@
|
|||||||
|
# Documentation Guidelines
|
||||||
|
|
||||||
|
This document is focused on documenting parts of the Pezkuwi SDK that relate to its external API. The list of such
|
||||||
|
crates can be found in [CODEOWNERS](/.github/CODEOWNERS). Search for the crates auto-assigned to the `docs-audit` team.
|
||||||
|
|
||||||
|
These crates are used by external developers and need thorough documentation. They are the most concerned with FRAME
|
||||||
|
development.
|
||||||
|
|
||||||
|
- [Documentation Guidelines](#documentation-guidelines)
|
||||||
|
- [General/Non-Pallet Crates](#generalnon-pallet-crates)
|
||||||
|
- [What to Document?](#what-to-document)
|
||||||
|
- [Rust Docs vs. Code Comments](#rust-docs-vs-code-comments)
|
||||||
|
- [How to Document?](#how-to-document)
|
||||||
|
- [TLDR](#tldr)
|
||||||
|
- [Proc-Macros](#proc-macros)
|
||||||
|
- [Other Guidelines](#other-guidelines)
|
||||||
|
- [Document Through Code](#document-through-code)
|
||||||
|
- [Formatting Matters](#formatting-matters)
|
||||||
|
- [Pallet Crates](#pallet-crates)
|
||||||
|
- [Top Level Pallet Docs (`lib.rs`)](#top-level-pallet-docs-librs)
|
||||||
|
- [Pezkuwi and Substrate](#pezkuwi-and-substrate)
|
||||||
|
- [Dispatchables](#dispatchables)
|
||||||
|
- [Storage Items](#storage-items)
|
||||||
|
- [Errors and Events](#errors-and-events)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## General/Non-Pallet Crates
|
||||||
|
|
||||||
|
First, consider the case for all such crates, except for those that are pallets.
|
||||||
|
|
||||||
|
### What to Document?
|
||||||
|
|
||||||
|
The first question is, what should you document? Use this filter:
|
||||||
|
|
||||||
|
1. In the crates assigned to `docs-audit` in [CODEOWNERS](/.github/CODEOWNERS),
|
||||||
|
2. All `pub` items need to be documented. If not `pub`, it doesn't appear in the rust-docs, and is not public facing.
|
||||||
|
|
||||||
|
- Within `pub` items, sometimes they are only `pub` to be used by another internal crate, and you can foresee that
|
||||||
|
this won't be used by anyone else. These need **not** be documented thoroughly.
|
||||||
|
|
||||||
|
- Reminder: `trait` items are public by definition if the trait is public.
|
||||||
|
|
||||||
|
3. All public modules (`mod`) should have reasonable module-level documentation (`//!`).
|
||||||
|
|
||||||
|
#### Rust Docs vs. Code Comments
|
||||||
|
|
||||||
|
Note that anything starting with `///` is an external rust-doc, and everything starting with `//` does not appear in the
|
||||||
|
rust-docs. It's important to not confuse the two in your documentation.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// Computes the square root of the input, returning `Ok(_)` if successful.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
// Details about the complexity, how you implemented this, and some quirks that
|
||||||
|
// are NOT relevant to the external interface, so it starts with '//'.
|
||||||
|
// This can also be moved inside the function.
|
||||||
|
pub fn sqrt(x: u32) -> Result<u32, ()> {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### How to Document?
|
||||||
|
|
||||||
|
There are good sources to look into:
|
||||||
|
|
||||||
|
- [Rust Documentation Guide](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html)
|
||||||
|
- [Documentation in Rust
|
||||||
|
Book](https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments)
|
||||||
|
- [Guide on Writing Documentation for a Rust
|
||||||
|
Crate](https://blog.guillaume-gomez.fr/articles/2020-03-12+Guide+on+how+to+write+documentation+for+a+Rust+crate)
|
||||||
|
|
||||||
|
As mentioned
|
||||||
|
[here](https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/documentation.html#writing-documentation-comments)
|
||||||
|
and [here](https://blog.guillaume-gomez.fr/articles/2020-03-12+Guide+on+how+to+write+documentation+for+a+Rust+crate),
|
||||||
|
always start with a **single sentence** demonstrating what is documented. All additional documentation should be added
|
||||||
|
*after a newline*. Strive to make the first sentence succinct and short.The reason for this is the first paragraph of
|
||||||
|
docs about an item (everything before the first newline) is used as the excerpt that rust doc displays about this item
|
||||||
|
when it appears in tables, such as the table listing all functions in a module. If this excerpt is too long, the module
|
||||||
|
docs will be very difficult to read.
|
||||||
|
|
||||||
|
About [special
|
||||||
|
sections](https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/documentation.html#special-sections),
|
||||||
|
we will most likely not need to think about panic and safety in any runtime related code. Our code is never `unsafe`,
|
||||||
|
and will (almost) never panic.
|
||||||
|
|
||||||
|
Use `# Examples` as much as possible. These are great ways to further demonstrate what your APIs are doing, and add free
|
||||||
|
test coverage. As an additional benefit, any code in rust-docs is treated as an "integration test",
|
||||||
|
which tests your crate in a different way than unit tests. So, it is both a win for "more documentation" and a win for
|
||||||
|
"more test coverage".
|
||||||
|
|
||||||
|
You can also consider having an `# Error` section optionally. Of course, this only applies if there is a `Result` being
|
||||||
|
returned, and if the `Error` variants are overly complicated.
|
||||||
|
|
||||||
|
Strive to include correct links to other items in your written docs as much as possible.
|
||||||
|
Read more about how to correctly use links in your rust-docs
|
||||||
|
[here](https://doc.rust-lang.org/rustdoc/write-documentation/linking-to-items-by-name.html#valid-links) and
|
||||||
|
[here](https://rust-lang.github.io/rfcs/1946-intra-rustdoc-links.html#additions-to-the-documentation-syntax).
|
||||||
|
In other words, avoid `` `some_func` `` and instead use ``[`some_func`]``.
|
||||||
|
|
||||||
|
> While you are linking, you might become conscious of the fact that you are in need of linking to (too many) foreign
|
||||||
|
items in order to explain your API. This is leaning more towards API-Design rather than documentation, but it is a
|
||||||
|
warning that the subject API might be slightly wrong. For example, most "glue" traits[^1] in `frame/support` should be
|
||||||
|
designed and documented without making hard assumptions about particular pallets that implement them.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### TLDR
|
||||||
|
|
||||||
|
0. Have the goal of enforcing `#![deny(missing_docs)]` mentally, even if it is not enforced by the compiler 🙈.
|
||||||
|
1. Start with a single, clear and concise sentence. Follow up with more context, after a newline, if needed.
|
||||||
|
2. Use examples as much as reasonably possible.
|
||||||
|
3. Use links as much as possible.
|
||||||
|
4. Think about context. If you are explaining a lot of foreign topics while documenting a trait that should not
|
||||||
|
explicitly depend on them, you have likely not designed it properly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Proc-Macros
|
||||||
|
|
||||||
|
Note that there are special considerations when documenting proc macros. Doc links will appear to function *within* your
|
||||||
|
proc macro crate, but often will no longer function when these proc macros are re-exported elsewhere in your project.
|
||||||
|
The exception is doc links to *other proc macros* which will function just fine if they are also being re-exported. It
|
||||||
|
is also often necessary to disambiguate between a proc macro and a function of the same name, which can be done using
|
||||||
|
the `macro@my_macro_name` syntax in your link. Read more about how to correctly use links in your rust-docs
|
||||||
|
[here](https://doc.rust-lang.org/rustdoc/write-documentation/linking-to-items-by-name.html#valid-links) and
|
||||||
|
[here](https://rust-lang.github.io/rfcs/1946-intra-rustdoc-links.html#additions-to-the-documentation-syntax).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Other Guidelines
|
||||||
|
|
||||||
|
The above five guidelines must always be reasonably respected in the documentation.
|
||||||
|
|
||||||
|
The following is a set of notes that may not necessarily hold in all circumstances:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Document Through Code
|
||||||
|
|
||||||
|
You should make sure that your code is properly-named and well-organized so that your code functions as a form of
|
||||||
|
documentation. However, within the complexity of our projects in Pezkuwi/Substrate that is not enough. Particularly,
|
||||||
|
things like examples, errors and panics cannot be documented only through properly-named and well-organized code.
|
||||||
|
|
||||||
|
> Our north star is self-documenting code that also happens to be well-documented and littered with examples.
|
||||||
|
|
||||||
|
- Your written documents should *complement* the code, not *repeat* it. As an example, a documentation on top of a code
|
||||||
|
example should never look like the following:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// Sends request and handles the response.
|
||||||
|
trait SendRequestAndHandleResponse {
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above example, the documentation has added no useful information not already contained within the properly-named
|
||||||
|
trait and is redundant.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Formatting Matters
|
||||||
|
|
||||||
|
The way you format your documents (newlines, heading and so on) makes a difference. Consider the below examples:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// This function works with input u32 x and multiplies it by two. If
|
||||||
|
/// we optimize the other variant of it, we would be able to achieve more
|
||||||
|
/// efficiency but I have to think about it. Probably can panic if the input
|
||||||
|
/// overflows u32.
|
||||||
|
fn multiply_by_2(x: u32) -> u32 { .. }
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// Multiplies an input of type [`u32`] by two.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the input overflows.
|
||||||
|
///
|
||||||
|
/// # Complexity
|
||||||
|
///
|
||||||
|
/// Is implemented using some algorithm that yields complexity of O(1).
|
||||||
|
// More efficiency can be achieved if we improve this via such and such.
|
||||||
|
fn multiply_by_2(x: u32) -> u32 { .. }
|
||||||
|
```
|
||||||
|
|
||||||
|
They are both roughly conveying the same set of facts, but one is easier to follow because it was formatted cleanly.
|
||||||
|
Especially for traits and types that you can foresee will be seen and used a lot, try and write a well formatted
|
||||||
|
version.
|
||||||
|
|
||||||
|
Similarly, make sure your comments are wrapped at 100 characters line-width (as defined by our
|
||||||
|
[`rustfmt.toml`](../../.rustfmt.toml)), no **more and no less**! The more is fixed by `rustfmt` and our CI, but if you (for
|
||||||
|
some unknown reason) wrap your lines at 59 characters, it will pass the CI, and it will not look good 🫣. Consider using
|
||||||
|
a plugin like [rewrap](https://marketplace.visualstudio.com/items?itemName=stkb.rewrap) (for Visual Studio Code) to
|
||||||
|
properly do this.
|
||||||
|
|
||||||
|
[^1]: Those that help two pallets talk to each other.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pallet Crates
|
||||||
|
|
||||||
|
The guidelines so far have been general in nature, and are applicable to crates that are pallets and crates that are not
|
||||||
|
pallets.
|
||||||
|
|
||||||
|
The following is relevant to how to document parts of a crate that is a pallet. See
|
||||||
|
[`pallet-fast-unstake`](../../substrate/frame/fast-unstake/src/lib.rs) as one example of adhering these guidelines.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Top Level Pallet Docs (`lib.rs`)
|
||||||
|
|
||||||
|
For the top-level pallet docs, consider the following template:
|
||||||
|
|
||||||
|
```
|
||||||
|
//! # <Pallet Name>
|
||||||
|
//!
|
||||||
|
//! <single-liner about the pallet>.
|
||||||
|
//!
|
||||||
|
//! ## Pallet API
|
||||||
|
//!
|
||||||
|
//! <Reminder: inside the [`pallet`] module, a template that leads the reader to the relevant items is auto-generated. There is no need to repeat
|
||||||
|
//! things like "See Config trait for ...", which are generated inside [`pallet`] here anyways. You can use the line below as-is:>
|
||||||
|
//!
|
||||||
|
//! See the [`pallet`] module for more information about the interfaces this pallet exposes, including its
|
||||||
|
//! configuration trait, dispatchables, storage items, events and errors.
|
||||||
|
//!
|
||||||
|
//! ## Overview
|
||||||
|
//!
|
||||||
|
//! <should be high-level details that are relevant to the most broad audience>
|
||||||
|
//!
|
||||||
|
//! <The audience here is potentially non-coders who just want to know what this pallet does, not how it does it>
|
||||||
|
//!
|
||||||
|
//! <potentially a few paragraphs, focus on what external folks should know about the pallet>
|
||||||
|
//!
|
||||||
|
//! ### Example
|
||||||
|
//!
|
||||||
|
//! <Your pallet must have a few tests that cover important user journeys. Use https://crates.io/crates/docify to
|
||||||
|
//! reuse these as examples.>
|
||||||
|
//!
|
||||||
|
//! <The audience of this is those who want to know how this pallet works, to the extent of being able to build
|
||||||
|
//! something on top of it, like a DApp or another pallet. In some cases, you might want to add an example of how to
|
||||||
|
//! use this pallet in other pallets.>
|
||||||
|
//!
|
||||||
|
//! This section can most often be left as-is.
|
||||||
|
//!
|
||||||
|
//! ## Low Level / Implementation Details
|
||||||
|
//!
|
||||||
|
//! <The format of this section is up to you, but we suggest the Design-oriented approach that follows>
|
||||||
|
//!
|
||||||
|
//! <The audience of this would be your future self, or anyone who wants to gain a deep understanding of how the pallet
|
||||||
|
//! works so that they can eventually propose optimizations to it>
|
||||||
|
//!
|
||||||
|
//! ### Design Goals (optional)
|
||||||
|
//!
|
||||||
|
//! <Describe your goals with the pallet design.>
|
||||||
|
//!
|
||||||
|
//! ### Design (optional)
|
||||||
|
//!
|
||||||
|
//! <Describe how you've reached those goals. This should describe the storage layout of your pallet and what was your
|
||||||
|
//! approach in designing it that way.>
|
||||||
|
//!
|
||||||
|
//! ### Terminology (optional)
|
||||||
|
//!
|
||||||
|
//! <Optionally, explain any non-obvious terminology here. You can link to it if you want to use the terminology further
|
||||||
|
//! up>
|
||||||
|
```
|
||||||
|
|
||||||
|
This template's details (Heading 3s and beyond) are left flexible, and at the discretion of the developer to make the
|
||||||
|
best final choice about. For example, you might want to include `### Terminology` or not. Moreover, you might find it
|
||||||
|
more useful to include it in `## Overview`.
|
||||||
|
|
||||||
|
Nonetheless, the high level flow of going from the most high level explanation to the most low level explanation is
|
||||||
|
important to follow.
|
||||||
|
|
||||||
|
As a rule of thumb, the Heading 2s (`##`) in this template can be considered a strict rule, while the Heading 3s (`###`)
|
||||||
|
and beyond are flexible.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Pezkuwi and Substrate
|
||||||
|
|
||||||
|
Optionally, in order to demonstrate the relation between the two, you can start the pallet documentation with:
|
||||||
|
|
||||||
|
```
|
||||||
|
//! > Made with *Substrate*, for *Pezkuwi*.
|
||||||
|
//!
|
||||||
|
//! [![github]](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/substrate/frame/fast-unstake) -
|
||||||
|
//! [![pezkuwi]](https://pezkuwichain.io)
|
||||||
|
//!
|
||||||
|
//! [pezkuwi]: https://img.shields.io/badge/polkadot-E6007A?style=for-the-badge&logo=polkadot&logoColor=white
|
||||||
|
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Dispatchables
|
||||||
|
|
||||||
|
For each dispatchable (`fn` item inside `#[pallet::call]`), consider the following template:
|
||||||
|
|
||||||
|
```
|
||||||
|
/// <One-liner explaining what the dispatchable does>
|
||||||
|
///
|
||||||
|
/// ## Dispatch Origin
|
||||||
|
///
|
||||||
|
/// The dispatch origin of this call must be <details (e.g. Root, Signed, Unsigned)>
|
||||||
|
///
|
||||||
|
/// ## Details
|
||||||
|
///
|
||||||
|
/// <All other details, namely any errors that could occur within this dispatch and the events this dispatch could emit>
|
||||||
|
///
|
||||||
|
/// ## Errors (optional)
|
||||||
|
///
|
||||||
|
/// <If an extensive list of errors can be returned, list them individually instead of mentioning them in the section
|
||||||
|
/// above>
|
||||||
|
///
|
||||||
|
/// ## Events (optional)
|
||||||
|
///
|
||||||
|
/// <Events are akin to the "return type" of dispatchables, optionally mention them>
|
||||||
|
pub fn name_of_dispatchable(origin: OriginFor<T>, ...) -> DispatchResult {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Consider the fact that these docs will be part of the metadata of the associated dispatchable, and might be used by
|
||||||
|
wallets and explorers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Storage Items
|
||||||
|
|
||||||
|
1. If a map-like type is being used, always note the choice of your hashers as private code docs (`// Hasher X chosen
|
||||||
|
because ...`). Recall that this is not relevant information to external people, so it must be documented as `//`.
|
||||||
|
|
||||||
|
2. Consider explaining the crypto-economics of how a deposit is being taken in return of the storage being used.
|
||||||
|
|
||||||
|
3. Consider explaining why it is safe for the storage item to be unbounded, if `#[pallet::unbounded]` or
|
||||||
|
`#[pallet::without_storage_info]` is being used.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Errors and Events
|
||||||
|
|
||||||
|
Consider the fact that, similar to dispatchables, these docs will be part of the metadata of the associated event/error,
|
||||||
|
and might be used by wallets and explorers.
|
||||||
|
|
||||||
|
Specifically for `error`, explain why the error has happened, and what can be done in order to avoid it.
|
||||||
|
|
||||||
|
## Documenting Changes/PR
|
||||||
|
|
||||||
|
See [PRDoc](./prdoc.md).
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- A concise description of what your PR does and what issue it solves -->
|
||||||
|
<!-- Use GitHub semantic linking: Fixes #123, Closes #456 -->
|
||||||
|
|
||||||
|
## Type of Change
|
||||||
|
|
||||||
|
- [ ] Bug fix (non-breaking change that fixes an issue)
|
||||||
|
- [ ] New feature (non-breaking change that adds functionality)
|
||||||
|
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||||
|
- [ ] Pallet change (changes to custom pallets in `/pezkuwi/pallets/`)
|
||||||
|
- [ ] Runtime change (changes to runtime configuration)
|
||||||
|
- [ ] XCM/Cross-chain change
|
||||||
|
- [ ] Documentation update
|
||||||
|
- [ ] CI/CD change
|
||||||
|
|
||||||
|
## Changes Made
|
||||||
|
|
||||||
|
<!-- List the specific changes made in this PR -->
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
<!-- Describe the tests you ran and how to reproduce them -->
|
||||||
|
|
||||||
|
- [ ] Unit tests pass (`cargo test`)
|
||||||
|
- [ ] Build succeeds (`cargo build --release`)
|
||||||
|
- [ ] Benchmarks compile (`cargo build --release --features runtime-benchmarks`)
|
||||||
|
- [ ] Manual testing completed (describe below)
|
||||||
|
|
||||||
|
### Test Details
|
||||||
|
|
||||||
|
<!-- How did you test this change? -->
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] My code follows the project's style guidelines
|
||||||
|
- [ ] I have performed a self-review of my code
|
||||||
|
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||||
|
- [ ] I have made corresponding changes to the documentation
|
||||||
|
- [ ] My changes generate no new warnings
|
||||||
|
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||||
|
- [ ] New and existing unit tests pass locally with my changes
|
||||||
|
- [ ] Any dependent changes have been merged and published
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
<!-- For changes to pallets, runtime, or financial logic -->
|
||||||
|
|
||||||
|
- [ ] No new security vulnerabilities introduced
|
||||||
|
- [ ] Financial calculations reviewed for overflow/underflow
|
||||||
|
- [ ] Access control properly implemented
|
||||||
|
- [ ] No sensitive data exposed
|
||||||
|
|
||||||
|
## Breaking Changes
|
||||||
|
|
||||||
|
<!-- If this is a breaking change, describe the impact and migration path -->
|
||||||
|
|
||||||
|
N/A
|
||||||
|
|
||||||
|
## Related Issues/PRs
|
||||||
|
|
||||||
|
<!-- Link any related issues or PRs -->
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**For Reviewers:**
|
||||||
|
- Check that tests cover the changes adequately
|
||||||
|
- Verify no regressions in existing functionality
|
||||||
|
- For pallet changes: review weight calculations
|
||||||
|
- For XCM changes: verify cross-chain compatibility
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<!-- markdown-link-check-disable -->
|
||||||
|
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
Parity Technologies is committed to resolving security vulnerabilities in our
|
||||||
|
software quickly and carefully. We take the necessary steps to minimize risk,
|
||||||
|
provide timely information, and deliver vulnerability fixes and mitigations
|
||||||
|
required to address security issues.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Security vulnerabilities in Parity software should be reported by email to
|
||||||
|
security@parity.io. If you think your report might be eligible for the Parity
|
||||||
|
Bug Bounty Program, your email should be sent to bugbounty@parity.io.
|
||||||
|
|
||||||
|
Your report should include the following:
|
||||||
|
|
||||||
|
- your name
|
||||||
|
- description of the vulnerability
|
||||||
|
- attack scenario (if any)
|
||||||
|
- components
|
||||||
|
- reproduction
|
||||||
|
- other details
|
||||||
|
|
||||||
|
Try to include as much information in your report as you can, including a
|
||||||
|
description of the vulnerability, its potential impact, and steps for
|
||||||
|
reproducing it. Be sure to use a descriptive subject line.
|
||||||
|
|
||||||
|
You'll receive a response to your email within two business days indicating
|
||||||
|
the next steps in handling your report. We encourage finders to use encrypted
|
||||||
|
communication channels to protect the confidentiality of vulnerability reports.
|
||||||
|
You can encrypt your report using our public key. This key is [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73)
|
||||||
|
server and reproduced below.
|
||||||
|
|
||||||
|
After the initial reply to your report, our team will endeavor to keep you
|
||||||
|
informed of the progress being made towards a fix. These updates will be sent
|
||||||
|
at least every five business days.
|
||||||
|
|
||||||
|
Thank you for taking the time to responsibly disclose any vulnerabilities you find.
|
||||||
|
|
||||||
|
## Responsible Investigation and Reporting
|
||||||
|
|
||||||
|
Responsible investigation and reporting includes, but isn't limited to, the
|
||||||
|
following:
|
||||||
|
|
||||||
|
- Don't violate the privacy of other users, destroy data, etc.
|
||||||
|
- Don’t defraud or harm Parity Technologies Ltd or its users during your
|
||||||
|
research; you should make a good faith effort to not interrupt or degrade our
|
||||||
|
services.
|
||||||
|
- Don't target our physical security measures, or attempt to use social
|
||||||
|
engineering, spam, distributed denial of service (DDOS) attacks, etc.
|
||||||
|
- Initially report the bug only to us and not to anyone else.
|
||||||
|
- Give us a reasonable amount of time to fix the bug before disclosing it to
|
||||||
|
anyone else, and give us adequate written warning before disclosing it to
|
||||||
|
anyone else.
|
||||||
|
- In general, please investigate and report bugs in a way that makes a
|
||||||
|
reasonable, good faith effort not to be disruptive or harmful to us or our
|
||||||
|
users. Otherwise your actions might be interpreted as an attack rather than
|
||||||
|
an effort to be helpful.
|
||||||
|
|
||||||
|
## Bug Bounty Program
|
||||||
|
|
||||||
|
Our Bug Bounty Program allows us to recognize and reward members of the Parity
|
||||||
|
community for helping us find and address significant bugs, in accordance with
|
||||||
|
the terms of the Parity Bug Bounty Program. A detailed description on
|
||||||
|
eligibility, rewards, legal information and terms & conditions for contributors
|
||||||
|
can be found on [our website](https://paritytech.io/bug-bounty.html).
|
||||||
|
|
||||||
|
## Plaintext PGP Key
|
||||||
|
|
||||||
|
```
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQINBF0vHwQBEADKui4qAo4bzdzRhMm+uhUpYGf8jjjmET3zJ8kKQIpp6JTsV+HJ
|
||||||
|
6m1We0QYeMRXoOYH1xVHBf2zNCuHS0nSQdUCQA7SHWsPB05STa2hvlR7fSdQnCCp
|
||||||
|
gnLOJWXvvedlRDIAhvqI6cwLdUlXgVSKEwrwmrpiBhh4NxI3qX+LyIa+Ovkchu2S
|
||||||
|
d/YCnE4GqojSGRfJYiGwe2N+sF7OfaoKhQuTrtdDExHrMU4cWnTXW2wyxTr4xkj9
|
||||||
|
jS2WeLVZWflvkDHT8JD9N6jNxBVEF/Qvjk83zI0kCOzkhek8x+YUgfLq3/rHOYbX
|
||||||
|
3pW21ccHYPacHjHWvKE+xRebjeEhJ4KxKHfCVjQcxybwDBqDka1AniZt4CQ7UORf
|
||||||
|
MU/ue2oSZ9nNg0uMdb/0AbQPZ04OlMcYPAPWzFL08nVPox9wT9uqlL6JtcOeC90h
|
||||||
|
oOeDmfgwmjMmdwWTRgt9qQjcbgXzVvuAzIGbzj1X3MdLspWdHs/d2+US4nji1TkN
|
||||||
|
oYIW7vE+xkd3aB+NZunIlm9Rwd/0mSgDg+DaNa5KceOLhq0/qKgcXC/RRU29I8II
|
||||||
|
tusRoR/oesGJGYTjh4k6PJkG+nvDPsoQrwYT44bhnniS1xYkxWYXF99JFI7LgMdD
|
||||||
|
e1SgKeIDVpvm873k82E6arp5655Wod1XOjaXBggCwFp84eKcEZEN+1qEWwARAQAB
|
||||||
|
tClQYXJpdHkgU2VjdXJpdHkgVGVhbSA8c2VjdXJpdHlAcGFyaXR5LmlvPokCVAQT
|
||||||
|
AQoAPhYhBJ1LK264+XFW0ZZpqf8IEtSRuWeYBQJdLx8EAhsDBQkDwmcABQsJCAcC
|
||||||
|
BhUKCQgLAgQWAgMBAh4BAheAAAoJEP8IEtSRuWeYL84QAI6NwnwS561DWYYRAd4y
|
||||||
|
ocGPr3CnwFSt1GjkSkRy3B+tMhzexBg1y7EbLRUefIrO4LwOlywtRk8tTRGgEI4i
|
||||||
|
5xRLHbOkeolfgCFSpOj5d8cMKCt5HEIv18hsv6dkrzlSYA5NLX/GRBEh3F/0sGny
|
||||||
|
vCXapfxa1cx72sU7631JBK7t2Tf+MfwxdfyFZ9TI9WdtP5AfVjgTkIVkEDFcZPTc
|
||||||
|
n3CYXqTYFIBCNUD8LP4iTi3xUt7pTGJQQoFT8l15nJCgzRYQ+tXpoTRlf+/LtXmw
|
||||||
|
6iidPV87E06jHdK9666rBouIabAtx7i0/4kwo+bSZ8DiSKRUaehiHGd212HSEmdF
|
||||||
|
jxquWE4pEzoUowYznhSIfR+WWIqRBHxEYarP4m98Hi+VXZ7Fw1ytzO8+BAKnLXnj
|
||||||
|
2W2+T9qJks5gqVEoaWNnqpvya6JA11QZvZ0w7Om2carDc2ILNm2Xx9J0mRUye8P0
|
||||||
|
KxcgqJuKNGFtugebQAsXagkxOKsdKna1PlDlxEfTf6AgI3ST8qSiMAwaaIMB/REF
|
||||||
|
VKUapGoslQX4tOCjibI2pzEgE//D8NAaSVu2A9+BUcFERdZRxsI7fydIXNeZ2R46
|
||||||
|
N2qfW+DP3YR/14QgdRxDItEavUoE1vByRXwIufKAkVemOZzIoFXKFsDeXwqTVW5i
|
||||||
|
6CXu6OddZ3QHDiT9TEbRny4QuQINBF0vKCwBEACnP5J7LEGbpxNBrPvGdxZUo0YA
|
||||||
|
U8RgeKDRPxJTvMo27V1IPZGaKRCRq8LBfg/eHhqZhQ7SLJBjBljd8kuT5dHDBTRe
|
||||||
|
jE1UIOhmnlSlrEJjAmpVO08irlGpq1o+8mGcvkBsR0poCVjeNeSnwYfRnR+c3GK5
|
||||||
|
Er6/JRqfN4mJvnEC9/Pbm6C7ql6YLKxC3yqzF97JL5brbbuozrW7nixY/yAI8619
|
||||||
|
VlBIMP7PAUbGcnSQyuV5b/Wr2Sgr6NJclnNSLjh2U9/Du6w/0tDGlMBts8HjRnWJ
|
||||||
|
BXbkTdQKCTaqgK68kTKSiN1/x+lynxHC2AavMpH/08Kopg2ZCzJowMKIgcB+4Z/I
|
||||||
|
DJKZWHWKumhaZMGXcWgzgcByog9IpamuROEZFJNEUAFf7YIncEckPSif4looiOdS
|
||||||
|
VurKZGvYXXaGSsZbGgHxI5CWu7ZxMdLBLvtOcCYmRQrG+g/h+PGU5BT0bNAfNTkm
|
||||||
|
V3/n1B/TWbpWRmB3AwT2emQivXHkaubGI0VivhaO43AuI9JWoqiMqFtxbuTeoxwD
|
||||||
|
xlu2Dzcp0v+AR4T5cIG9D5/+yiPc25aIY7cIKxuNFHIDL4td5fwSGC7vU6998PIG
|
||||||
|
2Y48TGBnw7zpEfDfMayqAeBjX0YU6PTNsvS5O6bP3j4ojTOUYD7Z8QdCvgISDID3
|
||||||
|
WMGAdmSwmCRvsQ/OJwARAQABiQI8BBgBCgAmFiEEnUsrbrj5cVbRlmmp/wgS1JG5
|
||||||
|
Z5gFAl0vKCwCGwwFCQB2pwAACgkQ/wgS1JG5Z5hdbw//ZqR+JcWm59NUIHjauETJ
|
||||||
|
sYDYhcAfa3txTacRn5uPz/TQiTd7wZ82+G8Et0ZnpEHy6eWyBqHpG0hiPhFBzxjY
|
||||||
|
nhjHl8jJeyo2mQIVJhzkL58BHBZk8WM2TlaU7VxZ6TYOmP2y3qf6FD6mCcrQ4Fml
|
||||||
|
E9f0lyVUoI/5Zs9oF0izRk8vkwaY3UvLM7XEY6nM8GnFG8kaiZMYmx26Zo7Uz31G
|
||||||
|
7EGGZFsrVDXfNhSJyz79Gyn+Lx9jOTdoR0sH/THYIIosE83awMGE6jKeuDYTbVWu
|
||||||
|
+ZtHQef+pRteki3wvNLJK+kC1y3BtHqDJS9Lqx0s8SCiVozlC+fZfC9hCtU7bXJK
|
||||||
|
0UJZ4qjSvj6whzfaNgOZAqJpmwgOnd8W/3YJk1DwUeX98FcU38MR23SOkx2EDdDE
|
||||||
|
77Kdu62vTs/tLmOTuyKBvYPaHaYulYjQTxurG+o8vhHtaL87ARvuq+83dj+nO5z3
|
||||||
|
5O9vkcVJYWjOEnJe7ZvCTxeLJehpCmHIbyUuDx5P24MWVbyXOxIlxNxTqlub5GlW
|
||||||
|
rQF6Qsa/0k9TRk7Htbct6fAA0/VahJS0g096MrTH8AxBXDNE8lIoNeGikVlaxK9Z
|
||||||
|
S+aannlWYIJymZ4FygIPPaRlzhAoXBuJd8OaR5giC7dS1xquxKOiQEXTGsLeGFaI
|
||||||
|
BZYiIhW7GG4ozvKDqyNm4eg=
|
||||||
|
=yKcB
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
```
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
---
|
||||||
|
title: Style Guide for Rust in the Pezkuwi-SDK
|
||||||
|
---
|
||||||
|
|
||||||
|
Where possible these styles are enforced by settings in `rustfmt.toml` so if you run `cargo +nightly fmt`
|
||||||
|
then you will adhere to most of these style guidelines automatically.
|
||||||
|
|
||||||
|
To see exactly which nightly version is used, check our CI job logs.
|
||||||
|
|
||||||
|
# Formatting
|
||||||
|
|
||||||
|
- Indent using tabs.
|
||||||
|
- Lines should be longer than 100 characters long only in exceptional circumstances and certainly
|
||||||
|
no longer than 120. For this purpose, tabs are considered 4 characters wide.
|
||||||
|
- Indent levels should be greater than 5 only in exceptional circumstances and certainly no
|
||||||
|
greater than 8. If they are greater than 5, then consider using `let` or auxiliary functions in
|
||||||
|
order to strip out complex inline expressions.
|
||||||
|
- Never have spaces on a line prior to a non-whitespace character
|
||||||
|
- Follow-on lines are only ever a single indent from the original line.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn calculation(some_long_variable_a: i8, some_long_variable_b: i8) -> bool {
|
||||||
|
let x = some_long_variable_a * some_long_variable_b
|
||||||
|
- some_long_variable_b / some_long_variable_a
|
||||||
|
+ sqrt(some_long_variable_a) - sqrt(some_long_variable_b);
|
||||||
|
x > 10
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Indent level should follow open parens/brackets, but should be collapsed to the smallest number
|
||||||
|
of levels actually used:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn calculate(
|
||||||
|
some_long_variable_a: f32,
|
||||||
|
some_long_variable_b: f32,
|
||||||
|
some_long_variable_c: f32,
|
||||||
|
) -> f32 {
|
||||||
|
(-some_long_variable_b + sqrt(
|
||||||
|
// two parens open, but since we open & close them both on the
|
||||||
|
// same line, only one indent level is used
|
||||||
|
some_long_variable_b * some_long_variable_b
|
||||||
|
- 4 * some_long_variable_a * some_long_variable_c
|
||||||
|
// both closed here at beginning of line, so back to the original indent
|
||||||
|
// level
|
||||||
|
)) / (2 * some_long_variable_a)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `where` is indented, and its items are indented one further.
|
||||||
|
- Argument lists or function invocations that are too long to fit on one line are indented
|
||||||
|
similarly to code blocks, and once one param is indented in such a way, all others should be,
|
||||||
|
too. Run-on parameter lists are also acceptable for single-line run-ons of basic function calls.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// OK
|
||||||
|
fn foo(
|
||||||
|
really_long_parameter_name_1: SomeLongTypeName,
|
||||||
|
really_long_parameter_name_2: SomeLongTypeName,
|
||||||
|
shrt_nm_1: u8,
|
||||||
|
shrt_nm_2: u8,
|
||||||
|
) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOT OK
|
||||||
|
fn foo(really_long_parameter_name_1: SomeLongTypeName, really_long_parameter_name_2: SomeLongTypeName,
|
||||||
|
shrt_nm_1: u8, shrt_nm_2: u8) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
{
|
||||||
|
// Complex line (not just a function call, also a let statement). Full
|
||||||
|
// structure.
|
||||||
|
let (a, b) = bar(
|
||||||
|
really_long_parameter_name_1,
|
||||||
|
really_long_parameter_name_2,
|
||||||
|
shrt_nm_1,
|
||||||
|
shrt_nm_2,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Long, simple function call.
|
||||||
|
waz(
|
||||||
|
really_long_parameter_name_1,
|
||||||
|
really_long_parameter_name_2,
|
||||||
|
shrt_nm_1,
|
||||||
|
shrt_nm_2,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Short function call. Inline.
|
||||||
|
baz(a, b);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Always end last item of a multi-line comma-delimited set with `,` when legal:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Point<T> {
|
||||||
|
x: T,
|
||||||
|
y: T, // <-- Multiline comma-delimited lists end with a trailing ,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single line comma-delimited items do not have a trailing `,`
|
||||||
|
enum Meal { Breakfast, Lunch, Dinner };
|
||||||
|
```
|
||||||
|
|
||||||
|
- Avoid trailing `;`s where unneeded.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
if condition {
|
||||||
|
return 1 // <-- no ; here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `match` arms may be either blocks or have a trailing `,` but not both.
|
||||||
|
- Blocks should not be used unnecessarily.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
match meal {
|
||||||
|
Meal::Breakfast => "eggs",
|
||||||
|
Meal::Lunch => { check_diet(); recipe() },
|
||||||
|
// Meal::Dinner => { return Err("Fasting") } // WRONG
|
||||||
|
Meal::Dinner => return Err("Fasting"),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Style
|
||||||
|
|
||||||
|
- Panickers require explicit proofs they don't trigger. Calling `unwrap` is discouraged. The
|
||||||
|
exception to this rule is test code. Avoiding panickers by restructuring code is preferred if
|
||||||
|
feasible.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mut target_path =
|
||||||
|
self.path().expect(
|
||||||
|
"self is instance of DiskDirectory;\
|
||||||
|
DiskDirectory always returns path;\
|
||||||
|
qed"
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
- Unsafe code requires explicit proofs just as panickers do. When introducing unsafe code,
|
||||||
|
consider trade-offs between efficiency on one hand and reliability, maintenance costs, and
|
||||||
|
security on the other. Here is a list of questions that may help evaluating the trade-off while
|
||||||
|
preparing or reviewing a PR:
|
||||||
|
- how much more performant or compact the resulting code will be using unsafe code,
|
||||||
|
- how likely is it that invariants could be violated,
|
||||||
|
- are issues stemming from the use of unsafe code caught by existing tests/tooling,
|
||||||
|
- what are the consequences if the problems slip into production.
|
||||||
|
|
||||||
|
# Manifest Formatting
|
||||||
|
|
||||||
|
We use [taplo](https://taplo.tamasfe.dev/) to enforce consistent TOML formatting.
|
||||||
|
|
||||||
|
You can install it with `cargo install taplo-cli` and format your code with `taplo format --config .config/taplo.toml`.
|
||||||
|
|
||||||
|
See the config file for the exact rules.
|
||||||
|
|
||||||
|
You may find useful
|
||||||
|
|
||||||
|
- [Taplo VSCode extension](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml)
|
||||||
|
- For NeoVim, [taplo is available with Mason](https://github.com/williamboman/mason-lspconfig.nvim#available-lsp-servers)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Running Commands in PRs
|
||||||
|
|
||||||
|
You can run commands in PRs by triggering it via comment. It will use the context of your PR and post the results back.
|
||||||
|
Note: it works only for members of the `paritytech` organization.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
`/cmd --help` to see all available commands and usage format
|
||||||
|
|
||||||
|
`/cmd <command> --help` to see the usage of a specific command
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
- `/cmd fmt` to format the code in the PR. It commits back with the formatted code (fmt) and configs (taplo).
|
||||||
|
|
||||||
|
- `/cmd bench` to generate weights for a runtime. Read more about [Weight Generation](weight-generation.md)
|
||||||
|
|
||||||
|
- `/cmd prdoc` to generate a prdoc for a PR. Read more about [PRDoc](prdoc.md)
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
|
||||||
|
1.`--quiet` to suppress the output of the command in the comments.
|
||||||
|
By default, the Start and End/Failure of the command will be commented with the link to a pipeline.
|
||||||
|
If you want to avoid, use this flag. Go to
|
||||||
|
[Action Tab](https://github.com/pezkuwichain/pezkuwi-sdk/actions/workflows/cmd.yml) to see the pipeline status.
|
||||||
|
|
||||||
|
3.`--clean` to clean up all yours and bot's comments in PR relevant to `/cmd` commands. If you run too many commands,
|
||||||
|
or they keep failing, and you're rerunning them again, it's handy to add this flag to keep a PR clean.
|
||||||
|
|
||||||
|
### Adding new Commands
|
||||||
|
|
||||||
|
Feel free to add new commands to the workflow, however **_note_** that triggered workflows will use the actions
|
||||||
|
from `main` (default) branch, meaning they will take effect only after the PR with new changes/command is merged.
|
||||||
|
If you want to test the new command, it's better to test in your fork and local-to-fork PRs, where you control
|
||||||
|
the default branch.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
The regex in cmd.yml is: `^(\/cmd )([-\/\s\w.=:]+)$` accepts only alphanumeric, space, "-", "/", "=", ":", "." chars.
|
||||||
|
|
||||||
|
`/cmd bench --runtime bridge-hub-zagros --pallet=pallet_name`
|
||||||
|
`/cmd prdoc --audience runtime_dev runtime_user --bump patch --force`
|
||||||
|
`/cmd update-ui --image=docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v202407161507 --clean`
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Using Containers
|
||||||
|
|
||||||
|
Using containers via **Podman** or **Docker** brings benefit, whether it is to build a container image or run a node
|
||||||
|
while keeping a minimum footprint on your local system.
|
||||||
|
|
||||||
|
This document mentions using `podman` or `docker`. Those are usually interchangeable and it is encouraged using
|
||||||
|
preferably **Podman**. If you have podman installed and want to use all the commands mentioned below, you can simply
|
||||||
|
create an alias with `alias docker=podman`.
|
||||||
|
|
||||||
|
There are a few options to build a node within a container and inject a binary inside an image.
|
||||||
|
|
||||||
|
## Parity built container image
|
||||||
|
|
||||||
|
Parity builds and publishes a container image that can be found as `docker.io/parity/pezkuwi-teyrchain`.
|
||||||
|
|
||||||
|
## Parity CI image
|
||||||
|
|
||||||
|
Parity maintains and uses internally a generic "CI" image that can be used as a base to build binaries: [Parity CI
|
||||||
|
container image](https://github.com/paritytech/dockerfiles/tree/main/ci-unified):
|
||||||
|
|
||||||
|
The command below allows building a Linux binary without having to even install Rust or any dependency locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export $(curl https://raw.githubusercontent.com/paritytech/polkadot-sdk/refs/heads/master/.github/env | tr -d '"')
|
||||||
|
docker run --rm -it \
|
||||||
|
-w /pezkuwi-sdk \
|
||||||
|
-v $(pwd):/pezkuwi-sdk \
|
||||||
|
$IMAGE \
|
||||||
|
cargo build --release --locked -p pezkuwi-teyrchain-bin --bin pezkuwi-teyrchain
|
||||||
|
sudo chown -R $(id -u):$(id -g) target/
|
||||||
|
```
|
||||||
|
|
||||||
|
To reproduce the clean CI environment locally you can use the following commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export $(curl https://raw.githubusercontent.com/paritytech/polkadot-sdk/refs/heads/master/.github/env | tr -d '"')
|
||||||
|
docker run -it --rm $IMAGE bash
|
||||||
|
root@e2ff8a3f347b:/builds# git clone https://github.com/pezkuwichain/pezkuwi-sdk.git && cd pezkuwi-sdk
|
||||||
|
root@e2ff8a3f347b:/builds# <your cargo command goes here>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Injected image
|
||||||
|
|
||||||
|
Injecting a binary inside a base image is the quickest option to get a working container image. This only works if you
|
||||||
|
were able to build a Linux binary, either locally, or using a container as described above.
|
||||||
|
|
||||||
|
After building a Linux binary (`pezkuwi-teyrchain`) with cargo or with Parity CI image as documented above, the
|
||||||
|
following command allows producing a new container image where the compiled binary is injected:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ARTIFACTS_FOLDER=./target/release /docker/scripts/build-injected.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Container build
|
||||||
|
|
||||||
|
Alternatively, you can build an image with a builder pattern. This options takes a while but offers a simple method for
|
||||||
|
anyone to get a working container image without requiring any of the Rust toolchain installed locally.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build \
|
||||||
|
--tag $OWNER/$IMAGE_NAME \
|
||||||
|
--file ./docker/dockerfiles/pezkuwi-teyrchain/pezkuwi-teyrchain_builder.Dockerfile .
|
||||||
|
```
|
||||||
|
|
||||||
|
You may then run your new container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -it \
|
||||||
|
$OWNER/$IMAGE_NAME \
|
||||||
|
--collator --tmp \
|
||||||
|
--execution wasm \
|
||||||
|
--chain /specs/asset-hub-zagros.json
|
||||||
|
```
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
# Using Containers
|
||||||
|
|
||||||
|
The following commands should work no matter if you use Docker or Podman. In general, Podman is recommended. All
|
||||||
|
commands are "engine neutral" so you can use the container engine of your choice while still being able to copy/paste
|
||||||
|
the commands below.
|
||||||
|
|
||||||
|
Let's start defining Podman as our engine:
|
||||||
|
```
|
||||||
|
ENGINE=podman
|
||||||
|
```
|
||||||
|
|
||||||
|
If you prefer to stick with Docker, use:
|
||||||
|
```
|
||||||
|
ENGINE=docker
|
||||||
|
```
|
||||||
|
|
||||||
|
## The easiest way
|
||||||
|
|
||||||
|
The easiest/faster option to run Pezkuwi in Docker is to use the latest release images. These are small images that use
|
||||||
|
the latest official release of the Pezkuwi binary, pulled from our Debian package.
|
||||||
|
|
||||||
|
**_The following examples are running on zagros chain and without SSL. They can be used to quick start and learn how
|
||||||
|
Pezkuwi needs to be configured. Please find out how to secure your node, if you want to operate it on the internet. Do
|
||||||
|
not expose RPC and WS ports, if they are not correctly configured._**
|
||||||
|
|
||||||
|
Let's first check the version we have. The first time you run this command, the Pezkuwi docker image will be
|
||||||
|
downloaded. This takes a bit of time and bandwidth, be patient:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ENGINE run --rm -it parity/pezkuwi:latest --version
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also pass any argument/flag that Pezkuwi supports:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ENGINE run --rm -it parity/pezkuwi:latest --chain zagros --name "PolkaDocker"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Once you are done experimenting and picking the best node name :) you can start Pezkuwi as daemon, exposes the Pezkuwi
|
||||||
|
ports and mount a volume that will keep your blockchain data locally. Make sure that you set the ownership of your local
|
||||||
|
directory to the Pezkuwi user that is used by the container.
|
||||||
|
|
||||||
|
Set user id 1000 and group id 1000, by running `chown 1000.1000 /my/local/folder -R` if you use a bind mount.
|
||||||
|
|
||||||
|
To start a Pezkuwi node on default rpc port 9933 and default p2p port 30333 use the following command. If you want to
|
||||||
|
connect to rpc port 9933, then must add Pezkuwi startup parameter: `--rpc-external`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ENGINE run -d -p 30333:30333 -p 9933:9933 \
|
||||||
|
-v /my/local/folder:/pezkuwi \
|
||||||
|
parity/pezkuwi:latest \
|
||||||
|
--chain zagros --rpc-external --rpc-cors all \
|
||||||
|
--name "PolkaDocker
|
||||||
|
```
|
||||||
|
|
||||||
|
If you also want to expose the webservice port 9944 use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ENGINE run -d -p 30333:30333 -p 9933:9933 -p 9944:9944 \
|
||||||
|
-v /my/local/folder:/pezkuwi \
|
||||||
|
parity/pezkuwi:latest \
|
||||||
|
--chain zagros --ws-external --rpc-external --rpc-cors all --name "PolkaDocker"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Docker compose
|
||||||
|
|
||||||
|
You can use the following docker-compose.yml file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
pezkuwi:
|
||||||
|
container_name: pezkuwi
|
||||||
|
image: parity/pezkuwi
|
||||||
|
ports:
|
||||||
|
- 30333:30333 # p2p port
|
||||||
|
- 9933:9933 # rpc port
|
||||||
|
- 9944:9944 # ws port
|
||||||
|
- 9615:9615 # Prometheus port
|
||||||
|
volumes:
|
||||||
|
- /my/local/folder:/pezkuwi
|
||||||
|
command: [
|
||||||
|
"--name", "PolkaDocker",
|
||||||
|
"--ws-external",
|
||||||
|
"--rpc-external",
|
||||||
|
"--prometheus-external",
|
||||||
|
"--rpc-cors", "all"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
With following `docker-compose.yml` you can set up a node and use `pezkuwi-js-apps` as the front end on port 80. After
|
||||||
|
starting the node use a browser and enter your Docker host IP in the URL field: _<http://[YOUR_DOCKER_HOST_IP]>_
|
||||||
|
|
||||||
|
```bash
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
pezkuwi:
|
||||||
|
container_name: pezkuwi
|
||||||
|
image: parity/pezkuwi
|
||||||
|
ports:
|
||||||
|
- 30333:30333 # p2p port
|
||||||
|
- 9933:9933 # rpc port
|
||||||
|
- 9944:9944 # ws port
|
||||||
|
- 9615:9615 # Prometheus port
|
||||||
|
command: [
|
||||||
|
"--name", "PolkaDocker",
|
||||||
|
"--ws-external",
|
||||||
|
"--rpc-external",
|
||||||
|
"--prometheus-external",
|
||||||
|
"--rpc-cors", "all"
|
||||||
|
]
|
||||||
|
|
||||||
|
pezkuwiui:
|
||||||
|
container_name: pezkuwiui
|
||||||
|
image: jacogr/pezkuwi-js-apps
|
||||||
|
environment:
|
||||||
|
- WS_URL=ws://[YOUR_DOCKER_HOST_IP]:9944
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
```
|
||||||
|
|
||||||
|
## Limiting Resources
|
||||||
|
|
||||||
|
Chain syncing will utilize all available memory and CPU power your server has to offer, which can lead to crashing.
|
||||||
|
|
||||||
|
If running on a low resource VPS, use `--memory` and `--cpus` to limit the resources used. E.g. To allow a maximum of
|
||||||
|
512MB memory and 50% of 1 CPU, use `--cpus=".5" --memory="512m"`. Read more about limiting a container's resources
|
||||||
|
[here](https://docs.docker.com/config/containers/resource_constraints).
|
||||||
|
|
||||||
|
|
||||||
|
## Build your own image
|
||||||
|
|
||||||
|
There are 3 options to build a Pezkuwi container image:
|
||||||
|
- using the builder image
|
||||||
|
- using the injected "Debian" image
|
||||||
|
- using the generic injected image
|
||||||
|
|
||||||
|
### Builder image
|
||||||
|
|
||||||
|
To get up and running with the smallest footprint on your system, you may use an existing Pezkuwi Container image.
|
||||||
|
|
||||||
|
You may also build a Pezkuwi container image yourself (it takes a while...) using the container specs
|
||||||
|
`docker/dockerfiles/pezkuwi/pezkuwi_builder.Dockerfile`.
|
||||||
|
|
||||||
|
### Debian injected
|
||||||
|
|
||||||
|
The Debian injected image is how the official Pezkuwi container image is produced. It relies on the Debian package that
|
||||||
|
is published upon each release. The Debian injected image is usually available a few minutes after a new release is
|
||||||
|
published. It has the benefit of relying on the GPG signatures embedded in the Debian package.
|
||||||
|
|
||||||
|
### Generic injected
|
||||||
|
|
||||||
|
For simple testing purposes, the easiest option for Pezkuwi and also random binaries, is to use the
|
||||||
|
`binary_injected.Dockerfile` container spec. This option is less secure since the injected binary is not checked at all
|
||||||
|
but it has the benefit to be simple. This option requires to already have a valid `pezkuwi` binary, compiled for Linux.
|
||||||
|
|
||||||
|
This binary is then simply copied inside the `parity/base-bin` image.
|
||||||
|
|
||||||
|
## Reporting issues
|
||||||
|
|
||||||
|
If you run into issues with Pezkuwi when using docker, please run the following command (replace the tag with the
|
||||||
|
appropriate one if you do not use latest):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ENGINE run --rm -it parity/pezkuwi:latest --version
|
||||||
|
```
|
||||||
|
|
||||||
|
This will show you the Pezkuwi version as well as the git commit ref that was used to build your container. You can now
|
||||||
|
paste the version information in a [new issue](https://github.com/paritytech/polkadot/issues/new/choose).
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Markdown linting
|
||||||
|
|
||||||
|
Since the introduction of [PR #1309](https://github.com/paritytech/polkadot-sdk/pull/1309), the markdown
|
||||||
|
files in this repository are checked by a linter for formatting and consistency.
|
||||||
|
|
||||||
|
The linter used is [`markdownlint`](https://github.com/DavidAnson/markdownlint) and can be installed locally on your
|
||||||
|
machine. It can also be setup as [pre-commit hook](https://github.com/igorshubovych/markdownlint-cli#use-with-pre-commit)
|
||||||
|
to ensure that your markdown is passing all the tests.
|
||||||
|
|
||||||
|
The rules in place are defined
|
||||||
|
[here](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/.github/.markdownlint.yaml).
|
||||||
|
|
||||||
|
You may run `markdownlint` locally using:
|
||||||
|
```
|
||||||
|
markdownlint --config .github/.markdownlint.yaml --ignore target .
|
||||||
|
```
|
||||||
|
|
||||||
|
There are also plugins for your favorite editor, that can ensure that most
|
||||||
|
of the rules will pass and fix typical issues (such as trailing spaces,
|
||||||
|
missing eof new line, long lines, etc...)
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
# PRDoc
|
||||||
|
|
||||||
|
A [prdoc](https://github.com/paritytech/prdoc) is like a changelog but for a Pull Request. We use
|
||||||
|
this approach to record changes on a crate level. This information is then processed by the release
|
||||||
|
team to apply the correct crate version bumps and to generate the CHANGELOG of the next release.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
When creating a PR, the author needs to decide with the `R0-no-crate-publish-required` label whether the PR has to
|
||||||
|
contain a prdoc. The `R0` label should only be placed for No-OP changes like correcting a typo in a
|
||||||
|
comment or CI stuff. If unsure, ping the [CODEOWNERS](../../.github/CODEOWNERS) for advice.
|
||||||
|
|
||||||
|
## Auto Generation
|
||||||
|
|
||||||
|
You can create a PrDoc by using the `/cmd prdoc` command (see args with `/cmd prdoc --help`) in a
|
||||||
|
comment on your PR.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
- `audience` The audience of whom the changes may concern.
|
||||||
|
- `runtime_dev`: Anyone building a runtime themselves. For example teyrchain teams, or people
|
||||||
|
providing template runtimes. Also devs using pallets, FRAME etc directly. These are people who
|
||||||
|
care about the protocol (WASM), not the meta-protocol (client).
|
||||||
|
- `runtime_user`: Anyone using the runtime. Can be front-end devs reading the state, exchanges
|
||||||
|
listening for events, libraries that have hard-coded pallet indices etc. Anything that would
|
||||||
|
result in an observable change to the runtime behaviour must be marked with this.
|
||||||
|
- `node_dev`: Those who build around the client side code. Alternative client builders, SMOLDOT,
|
||||||
|
those who consume RPCs. These are people who are oblivious to the runtime changes. They only care
|
||||||
|
about the meta-protocol, not the protocol itself.
|
||||||
|
- `node_operator`: People who run the node. Think of validators, exchanges, indexer services, CI
|
||||||
|
actions. Anything that modifies how the binary behaves (its arguments, default arguments, error
|
||||||
|
messags, etc) must be marked with this.
|
||||||
|
- `bump:`: The default bump level for all crates. The PrDoc will likely need to be edited to reflect
|
||||||
|
the actual changes after generation. More details in the section below.
|
||||||
|
- `none`: There is no observable change. So to say: if someone were handed the old and the new
|
||||||
|
version of our software, it would be impossible to figure out what version is which.
|
||||||
|
- `patch`: Fixes that will never cause compilation errors if someone updates to this version. No
|
||||||
|
functionality has been changed. Should be limited to fixing bugs or No-OP implementation
|
||||||
|
changes.
|
||||||
|
- `minor`: Additions that will never cause compilation errors if someone updates to this version.
|
||||||
|
No functionality has been changed. Should be limited to adding new features.
|
||||||
|
- `major`: Anything goes.
|
||||||
|
- `force: true|false`: Whether to overwrite any existing PrDoc file.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/cmd prdoc --audience runtime_dev --bump patch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Local Generation
|
||||||
|
|
||||||
|
A `.prdoc` file is a YAML file with a defined structure (ie JSON Schema). Please follow these steps
|
||||||
|
to generate one:
|
||||||
|
|
||||||
|
1. Install the [`prdoc` CLI](https://github.com/paritytech/prdoc) by running `cargo install
|
||||||
|
parity-prdoc`.
|
||||||
|
1. Open a Pull Request and get the PR number.
|
||||||
|
1. Generate the file with `prdoc generate <PR_NUMBER>`. The output filename will be printed.
|
||||||
|
1. Optional: Install the `prdoc/schema_user.json` schema in your editor, for example
|
||||||
|
[VsCode](https://github.com/paritytech/prdoc?tab=readme-ov-file#schemas).
|
||||||
|
1. Edit your `.prdoc` file according to the [Audience](#pick-an-audience) and
|
||||||
|
[SemVer](#record-semver-changes) sections.
|
||||||
|
1. Check your prdoc with `prdoc check -n <PR_NUMBER>`. This is optional since the CI will also check
|
||||||
|
it.
|
||||||
|
|
||||||
|
> **Tip:** GitHub CLI and jq can be used to provide the number of your PR to generate the correct
|
||||||
|
> file:
|
||||||
|
> `prdoc generate $(gh pr view --json number | jq '.number') -o prdoc`
|
||||||
|
|
||||||
|
## Record SemVer Changes
|
||||||
|
|
||||||
|
All published crates that got modified need to have an entry in the `crates` section of your
|
||||||
|
`PRDoc`. This entry tells the release team how to bump the crate version prior to the next release.
|
||||||
|
It is very important that this information is correct, otherwise it could break the code of
|
||||||
|
downstream teams.
|
||||||
|
|
||||||
|
The bump can either be `major`, `minor`, `patch` or `none`. The three first options are defined by
|
||||||
|
[rust-lang.org](https://doc.rust-lang.org/cargo/reference/semver.html), whereas `None` should be
|
||||||
|
picked if no other applies. The `None` option is equivalent to the `R0-no-crate-publish-required` label, but on a crate
|
||||||
|
level. Experimental and private APIs are exempt from bumping and can be broken at any time. Please
|
||||||
|
read the [Crate Section](../RELEASE.md) of the RELEASE doc about them.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
For example when you modified two crates and record the changes:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
crates:
|
||||||
|
- name: frame-example
|
||||||
|
bump: major
|
||||||
|
- name: frame-example-pallet
|
||||||
|
bump: minor
|
||||||
|
```
|
||||||
|
|
||||||
|
It means that downstream code using `frame-example-pallet` is still guaranteed to work as before,
|
||||||
|
while code using `frame-example` might break.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
A crate that depends on another crate will automatically inherit its `major` bumps. This means that
|
||||||
|
you do not need to bump a crate that had a SemVer breaking change only from re-exporting another
|
||||||
|
crate with a breaking change.
|
||||||
|
`minor` an `patch` bumps do not need to be inherited, since `cargo` will automatically update them
|
||||||
|
to the latest compatible version.
|
||||||
|
|
||||||
|
### Overwrite CI Check
|
||||||
|
|
||||||
|
The `check-semver` CI check is doing sanity checks based on the provided `PRDoc` and the mentioned
|
||||||
|
crate version bumps. The tooling is not perfect and it may recommends incorrect bumps of the version.
|
||||||
|
The CI check can be forced to accept the provided version bump. This can be done like:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
crates:
|
||||||
|
- name: frame-example
|
||||||
|
bump: major
|
||||||
|
validate: false
|
||||||
|
- name: frame-example-pallet
|
||||||
|
bump: minor
|
||||||
|
```
|
||||||
|
|
||||||
|
By putting `validate: false` for `frame-example`, the version bump is ignored by the tooling. For
|
||||||
|
`frame-example-pallet` the version bump is still validated by the CI check.
|
||||||
|
|
||||||
|
### Backporting PRs
|
||||||
|
|
||||||
|
When [backporting changes](../BACKPORT.md) to a stable release branch (e.g. `stable2503`), stricter versioning rules
|
||||||
|
apply to minimise risk for downstream users.
|
||||||
|
|
||||||
|
#### ✅ Allowed Bump Levels
|
||||||
|
|
||||||
|
Only the following `bump` levels are allowed by default:
|
||||||
|
|
||||||
|
- `none`: No observable change. No detectable difference between old and new versions.
|
||||||
|
- `patch`: Bug fixes or internal changes. Do not affect functionality or cause compilation errors.
|
||||||
|
- `minor`: Backward-compatible additions. Safe to adopt; adds features only, no behaviour changes.
|
||||||
|
|
||||||
|
Backport PRs with `major` bumps will fail CI.
|
||||||
|
|
||||||
|
#### 🚨 Overriding the CI Check
|
||||||
|
|
||||||
|
If a `major` bump is truly needed, you must:
|
||||||
|
|
||||||
|
1. Set `validate: false` in the `.prdoc`. See [Overwrite CI Check](#overwrite-ci-check).
|
||||||
|
2. Include a justification in the PR description explaining:
|
||||||
|
- Why the bump is necessary.
|
||||||
|
- Why it is safe for downstream users.
|
||||||
|
3. Notify a release engineer or senior reviewer for approval.
|
||||||
|
|
||||||
|
> Use this override sparingly, and only when you’re confident the change is safe and justified.
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# Weight Generation
|
||||||
|
|
||||||
|
To generate weights for a runtime.
|
||||||
|
Weights generation is using self-hosted runner which is provided by Parity CI, the rest commands are using standard
|
||||||
|
GitHub runners on `ubuntu-latest` or `ubuntu-20.04`.
|
||||||
|
Self-hosted runner for benchmarks (`parity-weights`) is configured to meet requirements of reference
|
||||||
|
hardware for running validators
|
||||||
|
https://wiki.network.pezkuwichain.io/docs/maintain-guides-how-to-validate-polkadot#reference-hardware
|
||||||
|
|
||||||
|
In a PR run the actions through comment:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/cmd bench --help # outputs the actual usage documentation with examples and supported runtimes
|
||||||
|
|
||||||
|
# or
|
||||||
|
|
||||||
|
/cmd --help # to see all available commands
|
||||||
|
```
|
||||||
|
|
||||||
|
To regenerate all weights (however it will take long,
|
||||||
|
so don't do it unless you really need it), run the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/cmd bench
|
||||||
|
```
|
||||||
|
|
||||||
|
To generate weights for all pallets in a particular runtime(s), run the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/cmd bench --runtime kusama pezkuwi
|
||||||
|
```
|
||||||
|
|
||||||
|
For Substrate pallets (supports sub-modules too):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/cmd bench --runtime dev --pallet pallet_asset_conversion_ops
|
||||||
|
```
|
||||||
|
|
||||||
|
> **📝 Note**: The action is not being run right-away, it will be queued and run in the next available runner.
|
||||||
|
> So might be quick, but might also take up to 10 mins (That's in control of Github).
|
||||||
|
> Once the action is run, you'll see reaction 👀 on original comment, and if you didn't pass `--quiet` -
|
||||||
|
> it will also send a link to a pipeline when started, and link to whole workflow when finished.
|
||||||
|
>
|
||||||
|
> **📝 Note**: It will try keep benchmarking even if some pallets failed, with the result of failed/successful pallets.
|
||||||
|
>
|
||||||
|
> If you want to fail fast on first failed benchmark, add `--fail-fast` flag to the command.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This way it runs all possible runtimes for the specified pallets, if it finds them in the runtime
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/cmd bench --pallet pallet_balances pallet_xcm_benchmarks::generic pallet_xcm_benchmarks::fungible
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to run all specific pallet(s) for specific runtime(s), you can do it like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/cmd bench --runtime bridge-hub-pezkuwi --pallet pallet_xcm_benchmarks::generic pallet_xcm_benchmarks::fungible
|
||||||
|
```
|
||||||
|
|
||||||
|
> **💡Hint #1** : Sometimes when you run too many commands, or they keep failing and you're rerunning them again,
|
||||||
|
> it's handy to add `--clean` flag to the command. This will clean up all yours and bot's comments in PR relevant to
|
||||||
|
> /cmd commands.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/cmd bench --runtime kusama pezkuwi --pallet=pallet_balances --clean
|
||||||
|
```
|
||||||
|
|
||||||
|
> **💡Hint #2** : If you have questions or need help, feel free to tag @paritytech/opstooling (in github comments)
|
||||||
|
> or ping in [matrix](https://matrix.to/#/#command-bot:parity.io) channel.
|
||||||
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 634 KiB |
|
After Width: | Height: | Size: 5.0 MiB |
|
After Width: | Height: | Size: 5.0 MiB |
|
After Width: | Height: | Size: 601 KiB |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 355 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 586 KiB |
|
After Width: | Height: | Size: 742 KiB |
|
After Width: | Height: | Size: 750 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 429 KiB |
|
After Width: | Height: | Size: 666 KiB |
|
After Width: | Height: | Size: 632 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 670 KiB |
|
After Width: | Height: | Size: 725 KiB |
|
After Width: | Height: | Size: 319 KiB |
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 429 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 252 KiB |
@@ -0,0 +1,342 @@
|
|||||||
|
# Introduction to Pezkuwi Network
|
||||||
|
|
||||||
|
Welcome to Pezkuwi Network - a next-generation blockchain ecosystem built on Substrate, designed to empower communities with decentralized governance, identity management, and economic sovereignty.
|
||||||
|
|
||||||
|
**Last Updated:** 2025-12-10
|
||||||
|
**Version:** 3.0.0
|
||||||
|
**Status:** PHASE 1 COMPLETE | BENCHMARKS COMPLETE | PHASE 2 IN PROGRESS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Overview](#1-overview)
|
||||||
|
2. [Tokenomics Architecture](#2-tokenomics-architecture)
|
||||||
|
3. [Network Topology](#3-network-topology)
|
||||||
|
4. [Technical Implementation](#4-technical-implementation)
|
||||||
|
5. [Development Roadmap](#5-development-roadmap)
|
||||||
|
6. [Security Framework](#6-security-framework)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
### 1.1. Mission
|
||||||
|
|
||||||
|
Pezkuwi Network aims to provide a secure, consistent, and scalable blockchain infrastructure that enables:
|
||||||
|
|
||||||
|
- **Decentralized Governance** through the Welati pallet
|
||||||
|
- **Identity & Citizenship** via zero-knowledge KYC verification
|
||||||
|
- **Community-Driven Economics** with dual-token system
|
||||||
|
- **Education Platform** through Perwerde pallet
|
||||||
|
- **Trust-Based Networking** with reputation scoring
|
||||||
|
|
||||||
|
### 1.2. Core Features
|
||||||
|
|
||||||
|
| Feature | Description | Status |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Dual Token System** | HEZ (native gas) and PEZ (governance) tokens | ✅ Complete |
|
||||||
|
| **Asset Hub** | PEZ (ID:1) and wHEZ (ID:2) tokens at genesis | ✅ Complete |
|
||||||
|
| **Identity Bootstrap** | Founder citizenship with IdentityKyc initialization | ✅ Complete |
|
||||||
|
| **Validator Infrastructure** | TNPoS consensus with configurable validator sets | ✅ Complete |
|
||||||
|
| **Treasury System** | PezTreasury pallet with 5B PEZ initial supply | ✅ Complete |
|
||||||
|
| **Synthetic Halving** | 48-month halving mechanism for reward distribution | ✅ Complete |
|
||||||
|
|
||||||
|
### 1.3. Network Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ PEZKUWICHAIN RELAY CHAIN │
|
||||||
|
│ (100 Validators - Mainnet) │
|
||||||
|
│ │
|
||||||
|
│ Native Token: HEZ (Inflationary) │
|
||||||
|
│ Consensus: TNPoS (Trust-enhanced NPoS) │
|
||||||
|
│ Block Time: ~6 seconds │
|
||||||
|
└─────────────────────┬───────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌─────────────┼─────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
||||||
|
│ ASSET HUB │ │ PEOPLE CHAIN │ │ BRIDGE HUB │
|
||||||
|
│ (ParaId:1000)│ │ (ParaId:1004) │ │ (ParaId:1002) │
|
||||||
|
├───────────────┤ ├───────────────┤ ├───────────────┤
|
||||||
|
│ PEZ (ID:1) │ │ IdentityKyc │ │ XCM Bridge │
|
||||||
|
│ wHEZ (ID:2) │ │ Welati (Gov) │ │ Zagros Link │
|
||||||
|
│ Presale │ │ Perwerde │ │ │
|
||||||
|
│ TokenWrapper │ │ Trust │ │ │
|
||||||
|
│ PezTreasury │ │ │ │ │
|
||||||
|
└───────────────┘ └───────────────┘ └───────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Tokenomics Architecture
|
||||||
|
|
||||||
|
### 2.1. Dual Token System
|
||||||
|
|
||||||
|
#### HEZ Token (Native - Relay Chain)
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| Type | Native Balance (Inflationary) |
|
||||||
|
| **Genesis Supply** | **200,000,000 HEZ** (200M) |
|
||||||
|
| Inflation | Dynamic NPoS per era (~10%/year target) |
|
||||||
|
| Usage | Gas, Staking, Transaction Fees |
|
||||||
|
| Decimals | 12 |
|
||||||
|
| Unit | 1 HEZ = 10^12 Planck |
|
||||||
|
|
||||||
|
### 2.2. HEZ Genesis Distribution
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
|
│ HEZ GENESIS SUPPLY: 200,000,000 │
|
||||||
|
├────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ██████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ Founder 10% │
|
||||||
|
│ ██████████████████████████████████████░░ Presale 50% │
|
||||||
|
│ ████████████████░░░░░░░░░░░░░░░░░░░░░░░░ Gov Treasury 20% │
|
||||||
|
│ ████████████████░░░░░░░░░░░░░░░░░░░░░░░░ Airdrop 20% │
|
||||||
|
│ │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
| Category | Percentage | Amount (HEZ) | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Founder | 10% | 20,000,000 | Project founder allocation |
|
||||||
|
| Presale | 50% | 100,000,000 | Early investors |
|
||||||
|
| Government Treasury | 20% | 40,000,000 | Governance fund |
|
||||||
|
| Airdrop | 20% | 40,000,000 | Community distribution |
|
||||||
|
| **TOTAL** | **100%** | **200,000,000** | |
|
||||||
|
|
||||||
|
> **Note:** Post-genesis, HEZ supply increases ~10% annually through NPoS inflation.
|
||||||
|
|
||||||
|
#### PEZ Token (Asset - Asset Hub)
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| Asset ID | 1 |
|
||||||
|
| Type | pallet_assets (Fixed Supply) |
|
||||||
|
| Total Supply | **5,000,000,000 PEZ** (5 Billion) |
|
||||||
|
| Decimals | 12 |
|
||||||
|
| Unit | 1 PEZ = 10^12 Planck |
|
||||||
|
| Halving | Synthetic - 50% reduction every 48 months |
|
||||||
|
|
||||||
|
### 2.3. PEZ Token Distribution
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
|
│ PEZ TOTAL SUPPLY: 5,000,000,000 │
|
||||||
|
├────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ████████████████████░░░░░░░░░░░░░░░░░░░░ Treasury 20.25% │
|
||||||
|
│ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ Presale 1.875% │
|
||||||
|
│ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ Founder 1.875% │
|
||||||
|
│ ████████████████████████████████████████ Rewards 76.00% │
|
||||||
|
│ │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
| Category | Percentage | Amount (PEZ) | Vesting/Lock |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Treasury (Governance) | 20.25% | 1,012,500,000 | Governance controlled |
|
||||||
|
| Presale | 1.875% | 93,750,000 | Presale pallet managed |
|
||||||
|
| Founder | 1.875% | 93,750,000 | 4-year vesting |
|
||||||
|
| Validator/Nominator Rewards | 76.00% | 3,800,000,000 | Synthetic halving distribution |
|
||||||
|
|
||||||
|
### 2.4. Synthetic Halving Mechanism
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// pallet-pez-treasury: lib.rs
|
||||||
|
const HALVING_PERIOD_MONTHS: u32 = 48;
|
||||||
|
const INITIAL_EPOCH_REWARD: u128 = 79_166_666 * PEZ; // ~79M PEZ per month
|
||||||
|
|
||||||
|
fn calculate_epoch_reward(current_month: u32) -> u128 {
|
||||||
|
let halving_count = current_month / HALVING_PERIOD_MONTHS;
|
||||||
|
INITIAL_EPOCH_REWARD >> halving_count // Halves every 48 months
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Distribution Schedule:**
|
||||||
|
|
||||||
|
| Year | Monthly Distribution | Annual Total | Cumulative |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 1-4 | ~79.17M PEZ | ~950M PEZ | ~3.8B PEZ |
|
||||||
|
| 5-8 | ~39.58M PEZ | ~475M PEZ | ~4.75B PEZ |
|
||||||
|
| 9-12 | ~19.79M PEZ | ~237.5M PEZ | ~4.99B PEZ |
|
||||||
|
| 13+ | Decreasing | ... | ~5B PEZ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Network Topology
|
||||||
|
|
||||||
|
### 3.1. Validator Roadmap
|
||||||
|
|
||||||
|
| Phase | Validator Count | Collators (Parachain) | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Dev | 1 | 1 | Single node development |
|
||||||
|
| Local | 2 | 2 | Alice + Bob |
|
||||||
|
| Alpha | 4 | 2 | Core team testing |
|
||||||
|
| Beta | 8 | 4 | Real keys testing |
|
||||||
|
| Staging | 21 | 8 | Performance testing |
|
||||||
|
| Mainnet | 100 | 16 | Live network launch |
|
||||||
|
|
||||||
|
### 3.2. Parachain IDs
|
||||||
|
|
||||||
|
| Parachain | ID | Status |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Asset Hub Pezkuwichain | 1000 | ✅ Defined |
|
||||||
|
| Bridge Hub Pezkuwichain | 1002 | ✅ Complete |
|
||||||
|
| People Pezkuwichain | 1004 | ✅ Defined |
|
||||||
|
| Coretime/Broker | 1005 | ✅ Defined |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Technical Implementation
|
||||||
|
|
||||||
|
### 4.1. Custom Pallets
|
||||||
|
|
||||||
|
Pezkuwi Network includes 12 custom pallets:
|
||||||
|
|
||||||
|
| # | Pallet | Purpose | Benchmarks |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 1 | pallet-presale | Token launch platform | ✅ Complete |
|
||||||
|
| 2 | pallet-identity-kyc | KYC verification (6 extrinsics) | ✅ Complete |
|
||||||
|
| 3 | pallet-welati | Democratic governance | ✅ Complete |
|
||||||
|
| 4 | pallet-perwerde | Education platform (4 extrinsics) | ✅ Complete |
|
||||||
|
| 5 | pallet-pez-treasury | Community treasury | ✅ Complete |
|
||||||
|
| 6 | pallet-pez-rewards | Staking rewards (6 extrinsics) | ✅ Complete |
|
||||||
|
| 7 | pallet-validator-pool | Validator management | ✅ Complete |
|
||||||
|
| 8 | pallet-staking-score | Reputation metrics (1 extrinsic) | ✅ Complete |
|
||||||
|
| 9 | pallet-trust | P2P trust system (3 extrinsics) | ✅ Complete |
|
||||||
|
| 10 | pallet-referral | Referral incentives | ✅ Complete |
|
||||||
|
| 11 | pallet-tiki | NFT citizenship (4-tier) | ✅ Complete |
|
||||||
|
| 12 | pallet-token-wrapper | Cross-chain wrapping | ✅ Complete |
|
||||||
|
|
||||||
|
### 4.2. Asset Hub Genesis Configuration
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// AssetsConfig for PEZ and wHEZ
|
||||||
|
assets: AssetsConfig {
|
||||||
|
assets: vec![
|
||||||
|
(PEZ_ASSET_ID, treasury_account.clone(), true, 1),
|
||||||
|
(WHEZ_ASSET_ID, treasury_account.clone(), true, 1),
|
||||||
|
],
|
||||||
|
metadata: vec![
|
||||||
|
(PEZ_ASSET_ID, b"Pez Token".to_vec(), b"PEZ".to_vec(), 12),
|
||||||
|
(WHEZ_ASSET_ID, b"Wrapped HEZ".to_vec(), b"wHEZ".to_vec(), 12),
|
||||||
|
],
|
||||||
|
accounts: vec![
|
||||||
|
(PEZ_ASSET_ID, treasury_account.clone(), TREASURY_ALLOCATION + REWARDS_POOL),
|
||||||
|
(PEZ_ASSET_ID, founder_account.clone(), FOUNDER_ALLOCATION),
|
||||||
|
(PEZ_ASSET_ID, presale_account.clone(), PRESALE_ALLOCATION),
|
||||||
|
],
|
||||||
|
next_asset_id: Some(3),
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3. People Chain Genesis Configuration
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// IdentityKyc - Founder citizen
|
||||||
|
identity_kyc: IdentityKycConfig {
|
||||||
|
initial_citizens: vec![
|
||||||
|
(
|
||||||
|
founder_account.clone(),
|
||||||
|
pallet_identity_kyc::CitizenInfo {
|
||||||
|
kyc_level: pallet_identity_kyc::KycLevel::Full,
|
||||||
|
registration_block: 0,
|
||||||
|
referrer: None,
|
||||||
|
is_founder: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Development Roadmap
|
||||||
|
|
||||||
|
### 5.1. Phase 1: Core Infrastructure ✅ COMPLETE
|
||||||
|
|
||||||
|
| Task | Status |
|
||||||
|
| --- | --- |
|
||||||
|
| Asset Hub AssetsConfig implementation | ✅ Complete |
|
||||||
|
| People Chain IdentityKycConfig implementation | ✅ Complete |
|
||||||
|
| Relay Chain HEZ genesis distribution | ✅ Complete |
|
||||||
|
| Bridge Hub Parachain ID fix (1013→1002) | ✅ Complete |
|
||||||
|
| Dev preset compile tests | ✅ Complete |
|
||||||
|
| All custom pallet benchmarks | ✅ Complete |
|
||||||
|
| Weight generation (real values) | ✅ Complete |
|
||||||
|
|
||||||
|
### 5.2. Phase 2: Testnet Presets (In Progress)
|
||||||
|
|
||||||
|
| Task | Status |
|
||||||
|
| --- | --- |
|
||||||
|
| alpha_testnet preset | ⬜ Pending |
|
||||||
|
| beta_testnet preset (8 validators) | ⬜ Pending |
|
||||||
|
| Validator key integration | ⬜ Pending |
|
||||||
|
|
||||||
|
### 5.3. Phase 3: Staging & Mainnet
|
||||||
|
|
||||||
|
| Task | Status |
|
||||||
|
| --- | --- |
|
||||||
|
| staging_testnet preset (21 validators) | ⬜ Pending |
|
||||||
|
| mainnet preset (100 validators) | ⬜ Pending |
|
||||||
|
| Zombienet test scenarios | ⬜ Pending |
|
||||||
|
|
||||||
|
### 5.4. Phase 4: Validation & Documentation
|
||||||
|
|
||||||
|
| Task | Status |
|
||||||
|
| --- | --- |
|
||||||
|
| Build-spec tests for all presets | ⬜ Pending |
|
||||||
|
| E2E tests | ⬜ Pending |
|
||||||
|
| Operator documentation | ⬜ Pending |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Security Framework
|
||||||
|
|
||||||
|
### 6.1. Key Management Principles
|
||||||
|
|
||||||
|
1. **NEVER** write seed phrases in source code
|
||||||
|
2. **ONLY** use public key hex values in genesis
|
||||||
|
3. **NEVER** commit key files to git (.gitignore)
|
||||||
|
4. Store seed backups **OFFLINE** and **ENCRYPTED**
|
||||||
|
5. Protect mainnet keys with **HSM** or **Vault**
|
||||||
|
|
||||||
|
### 6.2. Pre-Mainnet Checklist
|
||||||
|
|
||||||
|
- [x] All keys verified and unique
|
||||||
|
- [x] No seed phrases in code
|
||||||
|
- [x] Key files in .gitignore
|
||||||
|
- [x] Treasury account under governance control
|
||||||
|
- [x] Copyright update complete
|
||||||
|
- [ ] Security audit complete
|
||||||
|
- [ ] Bug bounty program active
|
||||||
|
- [ ] Sudo removal plan ready
|
||||||
|
|
||||||
|
### 6.3. Contact & Support
|
||||||
|
|
||||||
|
- **Website:** https://pezkuwichain.io
|
||||||
|
- **Documentation:** https://docs.pezkuwichain.io
|
||||||
|
- **GitHub:** https://github.com/pezkuwichain
|
||||||
|
- **Technical Support:** Discord #validators-tech
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Ready to explore Pezkuwi Network? Here are your next steps:
|
||||||
|
|
||||||
|
1. **[SDK Documentation](/docs/sdk)** - Explore our Rust SDK
|
||||||
|
2. **[Whitepaper](/docs/whitepaper)** - Deep dive into our vision
|
||||||
|
3. **[GitHub](https://github.com/pezkuwichain/pezkuwi-sdk)** - View source code
|
||||||
|
4. **[Explorer](https://explorer.pezkuwichain.io)** - Browse the blockchain
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Welcome to the future of decentralized governance.*
|
||||||
|
|
||||||
|
---
|
||||||
|
*Last updated: 2025-12-10 | Version: 3.0.0*
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
flowchart TD
|
||||||
|
dot[pezkuwichain.io] --> devhub[pezkuwi_sdk_docs]
|
||||||
|
|
||||||
|
devhub --> pezkuwi_sdk
|
||||||
|
devhub --> reference_docs
|
||||||
|
devhub --> guides
|
||||||
|
devhub --> external_resources
|
||||||
|
|
||||||
|
pezkuwi_sdk --> substrate
|
||||||
|
pezkuwi_sdk --> frame
|
||||||
|
pezkuwi_sdk --> xcm
|
||||||
|
pezkuwi_sdk --> templates
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
flowchart TD
|
||||||
|
E(Extrinsic) ---> I(Inherent);
|
||||||
|
E --> T(Transaction)
|
||||||
|
T --> ST("Signed (aka. Transaction)")
|
||||||
|
T --> UT(Unsigned)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
flowchart LR
|
||||||
|
RuntimeCall --"TryInto"--> PalletCall
|
||||||
|
PalletCall --"Into"--> RuntimeCall
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
flowchart LR
|
||||||
|
|
||||||
|
subgraph Pezkuwi[The Pezkuwi Relay Chain]
|
||||||
|
PezkuwiNode[Pezkuwi Node]
|
||||||
|
PezkuwiRuntime[Pezkuwi Runtime]
|
||||||
|
end
|
||||||
|
|
||||||
|
FRAME -.-> PezkuwiRuntime
|
||||||
|
PezkuwiSDK[Pezkuwi SDK Node Libraries] -.-> PezkuwiNode
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
flowchart LR
|
||||||
|
subgraph PezkuwiSDKChain[A Pezkuwi SDK-based blockchain]
|
||||||
|
Node
|
||||||
|
Runtime
|
||||||
|
end
|
||||||
|
|
||||||
|
FRAME -.-> Runtime
|
||||||
|
PezkuwiSDK[Pezkuwi SDK Node Libraries] -.-> Node
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
flowchart LR
|
||||||
|
subgraph TeyrChain[A Pezkuwi TeyrChain]
|
||||||
|
TeyrChainNode[TeyrChain Node]
|
||||||
|
TeyrChainRuntime[TeyrChain Runtime]
|
||||||
|
end
|
||||||
|
|
||||||
|
FRAME -.-> TeyrChainRuntime
|
||||||
|
PezkuwiSDK[Pezkuwi SDK Node Libraries] -.-> TeyrChainNode
|
||||||
|
|
||||||
|
CumulusC[Cumulus Node Libraries] -.-> TeyrChainNode
|
||||||
|
CumulusR[Cumulus Runtime Libraries] -.-> TeyrChainRuntime
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
flowchart TB
|
||||||
|
subgraph Node[Node's View Of The State 🙈]
|
||||||
|
direction LR
|
||||||
|
0x1234 --> 0x2345
|
||||||
|
0x3456 --> 0x4567
|
||||||
|
0x5678 --> 0x6789
|
||||||
|
:code --> code[wasm code]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Runtime[Runtime's View Of The State 🙉]
|
||||||
|
direction LR
|
||||||
|
ab[alice's balance] --> abv[known value]
|
||||||
|
bb[bob's balance] --> bbv[known value]
|
||||||
|
cb[charlie's balance] --> cbv[known value]
|
||||||
|
c2[:code] --> c22[wasm code]
|
||||||
|
end
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
flowchart LR
|
||||||
|
%%{init: {'flowchart' : {'curve' : 'linear'}}}%%
|
||||||
|
subgraph BData[Blockchain Database]
|
||||||
|
direction LR
|
||||||
|
BN[Block N] -.-> BN1[Block N+1]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph SData[State Database]
|
||||||
|
direction LR
|
||||||
|
SN[State N] -.-> SN1[State N+1] -.-> SN2[State N+2]
|
||||||
|
end
|
||||||
|
|
||||||
|
BN --> STFN[STF]
|
||||||
|
SN --> STFN[STF]
|
||||||
|
STFN[STF] --> SN1
|
||||||
|
|
||||||
|
BN1 --> STFN1[STF]
|
||||||
|
SN1 --> STFN1[STF]
|
||||||
|
STFN1[STF] --> SN2
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
flowchart LR
|
||||||
|
B[Block] --> STF
|
||||||
|
S[State] --> STF
|
||||||
|
STF --> NS[New State]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
graph TB
|
||||||
|
subgraph Substrate
|
||||||
|
direction LR
|
||||||
|
subgraph Node
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Runtime
|
||||||
|
end
|
||||||
|
|
||||||
|
Node --runtime-api--> Runtime
|
||||||
|
Runtime --host-functions--> Node
|
||||||
|
end
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
flowchart LR
|
||||||
|
T[Using a Template] --> P[Writing Your Own FRAME-Based Pallet] --> C[Custom Node]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
graph TB
|
||||||
|
subgraph Substrate
|
||||||
|
direction LR
|
||||||
|
subgraph Node
|
||||||
|
end
|
||||||
|
subgraph Runtime
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
graph TB
|
||||||
|
subgraph Substrate
|
||||||
|
direction LR
|
||||||
|
subgraph Node
|
||||||
|
Database
|
||||||
|
Networking
|
||||||
|
Consensus
|
||||||
|
end
|
||||||
|
subgraph Runtime
|
||||||
|
subgraph FRAME
|
||||||
|
direction LR
|
||||||
|
Governance
|
||||||
|
Currency
|
||||||
|
Staking
|
||||||
|
Identity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Node --runtime-api--> Runtime
|
||||||
|
Runtime --host-functions--> Node
|
||||||
|
end
|
||||||
@@ -0,0 +1,687 @@
|
|||||||
|
# Pezkuwi SDK - Runtime Pallet Mapping
|
||||||
|
|
||||||
|
**Generated:** 2025-12-08
|
||||||
|
**Purpose:** Complete inventory of all pallets across production runtimes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- **Total Production Runtimes:** 9
|
||||||
|
- 2 Relay Chain Runtimes (PezkuwiChain, Zagros)
|
||||||
|
- 7 Teyrchain Runtimes (Asset Hub x2, People Chain x2, Test Teyrchain, Penpal, Test)
|
||||||
|
- **Total Custom Pallets:** 14
|
||||||
|
- **Custom Pallets with Benchmarks:** 9
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. PezkuwiChain Relay Chain Runtime
|
||||||
|
|
||||||
|
**Location:** `/home/mamostehp/Pezkuwi-SDK/pezkuwi/runtime/pezkuwichain/src/lib.rs`
|
||||||
|
**Spec Name:** `pezkuwichain`
|
||||||
|
**Spec Version:** 1_020_001
|
||||||
|
**Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### Pallets (Total: 61)
|
||||||
|
|
||||||
|
#### System & Core (Index 0-10)
|
||||||
|
- **System** (0) - frame_system
|
||||||
|
- **Babe** (1) - pallet_babe
|
||||||
|
- **Timestamp** (2) - pallet_timestamp
|
||||||
|
- **Indices** (3) - pallet_indices
|
||||||
|
- **Balances** (4) - pallet_balances
|
||||||
|
- **Authorship** (5) - pallet_authorship
|
||||||
|
- **Parameters** (6) - pallet_parameters
|
||||||
|
- **Offences** (7) - pallet_offences
|
||||||
|
- **Session** (8) - pallet_session
|
||||||
|
- **Staking** (9) - pallet_staking
|
||||||
|
- **Grandpa** (10) - pallet_grandpa
|
||||||
|
|
||||||
|
#### Consensus & Authority (Index 12-15)
|
||||||
|
- **AuthorityDiscovery** (12) - pallet_authority_discovery
|
||||||
|
- **FastUnstake** (15) - pallet_fast_unstake
|
||||||
|
|
||||||
|
#### Governance (Index 17-21, 43-44)
|
||||||
|
- **Council** (17) - pallet_collective::<Instance1>
|
||||||
|
- **Treasury** (18) - pallet_treasury
|
||||||
|
- **Claims** (19) - claims
|
||||||
|
- **ConvictionVoting** (20) - pallet_conviction_voting
|
||||||
|
- **Referenda** (21) - pallet_referenda
|
||||||
|
- **Origins** (43) - pallet_custom_origins
|
||||||
|
- **Whitelist** (44) - pallet_whitelist
|
||||||
|
|
||||||
|
#### Utility & Common (Index 24-33)
|
||||||
|
- **Utility** (24) - pallet_utility
|
||||||
|
- **Vesting** (28) - pallet_vesting
|
||||||
|
- **Scheduler** (29) - pallet_scheduler
|
||||||
|
- **Proxy** (30) - pallet_proxy
|
||||||
|
- **Multisig** (31) - pallet_multisig
|
||||||
|
- **Preimage** (32) - pallet_preimage
|
||||||
|
- **TransactionPayment** (33) - pallet_transaction_payment
|
||||||
|
- **Historical** (34) - session_historical
|
||||||
|
|
||||||
|
#### Teyrchains Support (Index 50-68)
|
||||||
|
- **TeyrchainsOrigin** (50) - teyrchains_origin
|
||||||
|
- **Configuration** (51) - teyrchains_configuration
|
||||||
|
- **ParasShared** (52) - teyrchains_shared
|
||||||
|
- **ParaInclusion** (53) - teyrchains_inclusion
|
||||||
|
- **ParaInherent** (54) - teyrchains_paras_inherent
|
||||||
|
- **ParaScheduler** (55) - teyrchains_scheduler
|
||||||
|
- **Paras** (56) - teyrchains_paras
|
||||||
|
- **Initializer** (57) - teyrchains_initializer
|
||||||
|
- **Dmp** (58) - teyrchains_dmp
|
||||||
|
- **Hrmp** (60) - teyrchains_hrmp
|
||||||
|
- **ParaSessionInfo** (61) - teyrchains_session_info
|
||||||
|
- **ParasDisputes** (62) - teyrchains_disputes
|
||||||
|
- **ParasSlashing** (63) - teyrchains_slashing
|
||||||
|
- **MessageQueue** (64) - pallet_message_queue
|
||||||
|
- **OnDemandAssignmentProvider** (66) - teyrchains_on_demand
|
||||||
|
- **CoretimeAssignmentProvider** (68) - teyrchains_assigner_coretime
|
||||||
|
|
||||||
|
#### Teyrchain Onboarding (Index 70-74)
|
||||||
|
- **Registrar** (70) - paras_registrar
|
||||||
|
- **Slots** (71) - slots
|
||||||
|
- **Auctions** (72) - auctions
|
||||||
|
- **Crowdloan** (73) - crowdloan
|
||||||
|
- **Coretime** (74) - coretime
|
||||||
|
|
||||||
|
#### 🔴 CUSTOM PEZKUWI PALLETS (Index 91)
|
||||||
|
- **ValidatorPool** (91) - pallet_validator_pool (TNPoS Shadow Mode)
|
||||||
|
|
||||||
|
#### Migrations & XCM (Index 98-99)
|
||||||
|
- **MultiBlockMigrations** (98) - pallet_migrations
|
||||||
|
- **XcmPallet** (99) - pallet_xcm
|
||||||
|
|
||||||
|
#### VoterBagsList (Index 100)
|
||||||
|
- **VoterBagsList** (100) - pallet_bags_list::<Instance1>
|
||||||
|
|
||||||
|
#### BEEFY & MMR (Index 240-242)
|
||||||
|
- **Beefy** (240) - pallet_beefy
|
||||||
|
- **Mmr** (241) - pallet_mmr
|
||||||
|
- **MmrLeaf** (242) - pallet_beefy_mmr
|
||||||
|
|
||||||
|
#### Testing & Admin (Index 249-255)
|
||||||
|
- **RootTesting** (249) - pallet_root_testing
|
||||||
|
- **ParasSudoWrapper** (250) - paras_sudo_wrapper
|
||||||
|
- **AssignedSlots** (251) - assigned_slots
|
||||||
|
- **ValidatorManager** (252) - validator_manager
|
||||||
|
- **StateTrieMigration** (254) - pallet_state_trie_migration
|
||||||
|
- **Sudo** (255) - pallet_sudo
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Zagros Relay Chain Runtime
|
||||||
|
|
||||||
|
**Location:** `/home/mamostehp/Pezkuwi-SDK/pezkuwi/runtime/zagros/src/lib.rs`
|
||||||
|
**Spec Name:** `zagros`
|
||||||
|
**Spec Version:** 1_020_001
|
||||||
|
**Status:** ⚠️ Zagros runtime does NOT exist in this codebase. Only PezkuwiChain relay chain is implemented.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Asset Hub PezkuwiChain Teyrchain Runtime
|
||||||
|
|
||||||
|
**Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/runtimes/assets/asset-hub-pezkuwichain/src/lib.rs`
|
||||||
|
**Spec Name:** `asset-hub-pezkuwichain`
|
||||||
|
**Spec Version:** 1_020_001
|
||||||
|
**Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### Pallets (Total: 43)
|
||||||
|
|
||||||
|
#### System Support (Index 0-5)
|
||||||
|
- **System** (0) - frame_system
|
||||||
|
- **TeyrchainSystem** (1) - cumulus_pallet_teyrchain_system
|
||||||
|
- **Timestamp** (3) - pallet_timestamp
|
||||||
|
- **TeyrchainInfo** (4) - teyrchain_info
|
||||||
|
- **WeightReclaim** (5) - cumulus_pallet_weight_reclaim
|
||||||
|
|
||||||
|
#### Monetary (Index 10-13)
|
||||||
|
- **Balances** (10) - pallet_balances
|
||||||
|
- **TransactionPayment** (11) - pallet_transaction_payment
|
||||||
|
- **AssetTxPayment** (13) - pallet_asset_conversion_tx_payment
|
||||||
|
|
||||||
|
#### Collator Support (Index 20-24)
|
||||||
|
- **Authorship** (20) - pallet_authorship
|
||||||
|
- **CollatorSelection** (21) - pallet_collator_selection
|
||||||
|
- **Session** (22) - pallet_session
|
||||||
|
- **Aura** (23) - pallet_aura
|
||||||
|
- **AuraExt** (24) - cumulus_pallet_aura_ext
|
||||||
|
|
||||||
|
#### XCM Helpers (Index 30-34, 45)
|
||||||
|
- **XcmpQueue** (30) - cumulus_pallet_xcmp_queue
|
||||||
|
- **PezkuwiXcm** (31) - pallet_xcm
|
||||||
|
- **CumulusXcm** (32) - cumulus_pallet_xcm
|
||||||
|
- **MessageQueue** (34) - pallet_message_queue
|
||||||
|
- **ToZagrosXcmRouter** (45) - pallet_xcm_bridge_hub_router::<Instance3>
|
||||||
|
|
||||||
|
#### Utilities (Index 40-42)
|
||||||
|
- **Utility** (40) - pallet_utility
|
||||||
|
- **Multisig** (41) - pallet_multisig
|
||||||
|
- **Proxy** (42) - pallet_proxy
|
||||||
|
|
||||||
|
#### Assets & NFTs (Index 50-62)
|
||||||
|
- **Assets** (50) - pallet_assets::<Instance1>
|
||||||
|
- **Uniques** (51) - pallet_uniques
|
||||||
|
- **Nfts** (52) - pallet_nfts
|
||||||
|
- **ForeignAssets** (53) - pallet_assets::<Instance2>
|
||||||
|
- **NftFractionalization** (54) - pallet_nft_fractionalization
|
||||||
|
- **PoolAssets** (55) - pallet_assets::<Instance3>
|
||||||
|
- **AssetConversion** (56) - pallet_asset_conversion
|
||||||
|
- **AssetsFreezer** (57) - pallet_assets_freezer::<Instance1>
|
||||||
|
- **ForeignAssetsFreezer** (58) - pallet_assets_freezer::<Instance2>
|
||||||
|
- **PoolAssetsFreezer** (59) - pallet_assets_freezer::<Instance3>
|
||||||
|
- **AssetRewards** (60) - pallet_asset_rewards
|
||||||
|
- **Nis** (61) - pallet_nis
|
||||||
|
- **AssetRate** (62) - pallet_asset_rate
|
||||||
|
|
||||||
|
#### Treasury & Bounties (Index 63-65)
|
||||||
|
- **Bounties** (63) - pallet_bounties
|
||||||
|
- **ChildBounties** (64) - pallet_child_bounties
|
||||||
|
- **Treasury** (65) - pallet_treasury
|
||||||
|
|
||||||
|
#### 🔴 CUSTOM PEZKUWI PALLETS (Index 70-73)
|
||||||
|
- **PezTreasury** (70) - pallet_pez_treasury ⭐ BENCHMARKED
|
||||||
|
- **Presale** (71) - pallet_presale ⭐ BENCHMARKED
|
||||||
|
- **TokenWrapper** (73) - pallet_token_wrapper ⭐ BENCHMARKED
|
||||||
|
|
||||||
|
#### Staking (Index 80-89)
|
||||||
|
- **Staking** (80) - pallet_staking_async
|
||||||
|
- **NominationPools** (81) - pallet_nomination_pools
|
||||||
|
- **VoterList** (83) - pallet_bags_list::<Instance1>
|
||||||
|
- **DelegatedStaking** (84) - pallet_delegated_staking
|
||||||
|
- **StakingRcClient** (89) - pallet_staking_async_rc_client
|
||||||
|
|
||||||
|
#### Multi-Block Election (Index 85-88)
|
||||||
|
- **MultiBlockElection** (85) - pallet_election_provider_multi_block
|
||||||
|
- **MultiBlockElectionVerifier** (86) - pallet_election_provider_multi_block::verifier
|
||||||
|
- **MultiBlockElectionUnsigned** (87) - pallet_election_provider_multi_block::unsigned
|
||||||
|
- **MultiBlockElectionSigned** (88) - pallet_election_provider_multi_block::signed
|
||||||
|
|
||||||
|
#### Migrations (Index 200)
|
||||||
|
- **AssetConversionMigration** (200) - pallet_asset_conversion_ops
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Asset Hub Zagros Teyrchain Runtime
|
||||||
|
|
||||||
|
**Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/runtimes/assets/asset-hub-zagros/src/lib.rs`
|
||||||
|
**Spec Name:** `asset-hub-zagros`
|
||||||
|
**Spec Version:** 1_020_001
|
||||||
|
**Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### Pallets (Total: 42)
|
||||||
|
|
||||||
|
#### System Support (Index 0-9)
|
||||||
|
- **System** (0) - frame_system
|
||||||
|
- **TeyrchainSystem** (1) - cumulus_pallet_teyrchain_system
|
||||||
|
- **Timestamp** (3) - pallet_timestamp
|
||||||
|
- **TeyrchainInfo** (4) - teyrchain_info
|
||||||
|
- **WeightReclaim** (5) - cumulus_pallet_weight_reclaim
|
||||||
|
- **MultiBlockMigrations** (6) - pallet_migrations
|
||||||
|
- **Preimage** (7) - pallet_preimage
|
||||||
|
- **Scheduler** (8) - pallet_scheduler
|
||||||
|
- **Sudo** (9) - pallet_sudo
|
||||||
|
|
||||||
|
#### Monetary (Index 10-14)
|
||||||
|
- **Balances** (10) - pallet_balances
|
||||||
|
- **TransactionPayment** (11) - pallet_transaction_payment
|
||||||
|
- **AssetTxPayment** (13) - pallet_asset_conversion_tx_payment
|
||||||
|
- **Vesting** (14) - pallet_vesting
|
||||||
|
|
||||||
|
#### Collator Support (Index 20-24)
|
||||||
|
- **Authorship** (20) - pallet_authorship
|
||||||
|
- **CollatorSelection** (21) - pallet_collator_selection
|
||||||
|
- **Session** (22) - pallet_session
|
||||||
|
- **Aura** (23) - pallet_aura
|
||||||
|
- **AuraExt** (24) - cumulus_pallet_aura_ext
|
||||||
|
|
||||||
|
#### XCM Helpers (Index 30-36)
|
||||||
|
- **XcmpQueue** (30) - cumulus_pallet_xcmp_queue
|
||||||
|
- **PezkuwiXcm** (31) - pallet_xcm
|
||||||
|
- **CumulusXcm** (32) - cumulus_pallet_xcm
|
||||||
|
- **ToPezkuwichainXcmRouter** (34) - pallet_xcm_bridge_hub_router::<Instance1>
|
||||||
|
- **MessageQueue** (35) - pallet_message_queue
|
||||||
|
- **SnowbridgeSystemFrontend** (36) - snowbridge_pallet_system_frontend
|
||||||
|
|
||||||
|
#### Utilities (Index 40-43)
|
||||||
|
- **Utility** (40) - pallet_utility
|
||||||
|
- **Multisig** (41) - pallet_multisig
|
||||||
|
- **Proxy** (42) - pallet_proxy
|
||||||
|
- **Indices** (43) - pallet_indices
|
||||||
|
|
||||||
|
#### Assets & NFTs (Index 50-61)
|
||||||
|
- **Assets** (50) - pallet_assets::<Instance1>
|
||||||
|
- **Uniques** (51) - pallet_uniques
|
||||||
|
- **Nfts** (52) - pallet_nfts
|
||||||
|
- **ForeignAssets** (53) - pallet_assets::<Instance2>
|
||||||
|
- **NftFractionalization** (54) - pallet_nft_fractionalization
|
||||||
|
- **PoolAssets** (55) - pallet_assets::<Instance3>
|
||||||
|
- **AssetConversion** (56) - pallet_asset_conversion
|
||||||
|
- **AssetsFreezer** (57) - pallet_assets_freezer::<Instance1>
|
||||||
|
- **ForeignAssetsFreezer** (58) - pallet_assets_freezer::<Instance2>
|
||||||
|
- **PoolAssetsFreezer** (59) - pallet_assets_freezer::<Instance3>
|
||||||
|
- **Revive** (60) - pallet_revive
|
||||||
|
- **AssetRewards** (61) - pallet_asset_rewards
|
||||||
|
|
||||||
|
#### State Trie Migration (Index 70)
|
||||||
|
- **StateTrieMigration** (70) - pallet_state_trie_migration
|
||||||
|
|
||||||
|
#### Staking (Index 80-89)
|
||||||
|
- **Staking** (80) - pallet_staking_async
|
||||||
|
- **NominationPools** (81) - pallet_nomination_pools
|
||||||
|
- **VoterList** (83) - pallet_bags_list::<Instance1>
|
||||||
|
- **DelegatedStaking** (84) - pallet_delegated_staking
|
||||||
|
- **StakingRcClient** (89) - pallet_staking_async_rc_client
|
||||||
|
- **MultiBlockElection** (85) - pallet_election_provider_multi_block
|
||||||
|
- **MultiBlockElectionVerifier** (86) - pallet_election_provider_multi_block::verifier
|
||||||
|
- **MultiBlockElectionUnsigned** (87) - pallet_election_provider_multi_block::unsigned
|
||||||
|
- **MultiBlockElectionSigned** (88) - pallet_election_provider_multi_block::signed
|
||||||
|
|
||||||
|
#### Governance (Index 90-95)
|
||||||
|
- **ConvictionVoting** (90) - pallet_conviction_voting
|
||||||
|
- **Referenda** (91) - pallet_referenda
|
||||||
|
- **Origins** (92) - pallet_custom_origins
|
||||||
|
- **Whitelist** (93) - pallet_whitelist
|
||||||
|
- **Treasury** (94) - pallet_treasury
|
||||||
|
- **AssetRate** (95) - pallet_asset_rate
|
||||||
|
|
||||||
|
#### Migrations (Index 200)
|
||||||
|
- **AssetConversionMigration** (200) - pallet_asset_conversion_ops
|
||||||
|
|
||||||
|
#### Admin Operations (Index 254)
|
||||||
|
- **AhOps** (254) - pallet_ah_ops
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. People PezkuwiChain Teyrchain Runtime
|
||||||
|
|
||||||
|
**Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/runtimes/people/people-pezkuwichain/src/lib.rs`
|
||||||
|
**Spec Name:** `people-pezkuwichain`
|
||||||
|
**Spec Version:** 1_020_001
|
||||||
|
**Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### Pallets (Total: 34)
|
||||||
|
|
||||||
|
#### System Support (Index 0-4)
|
||||||
|
- **System** (0) - frame_system
|
||||||
|
- **TeyrchainSystem** (1) - cumulus_pallet_teyrchain_system
|
||||||
|
- **Timestamp** (2) - pallet_timestamp
|
||||||
|
- **TeyrchainInfo** (3) - teyrchain_info
|
||||||
|
- **WeightReclaim** (4) - cumulus_pallet_weight_reclaim
|
||||||
|
|
||||||
|
#### Monetary (Index 10-12)
|
||||||
|
- **Balances** (10) - pallet_balances
|
||||||
|
- **TransactionPayment** (11) - pallet_transaction_payment
|
||||||
|
- **SkipFeelessPayment** (12) - pallet_skip_feeless_payment
|
||||||
|
|
||||||
|
#### Collator Support (Index 20-24)
|
||||||
|
- **Authorship** (20) - pallet_authorship
|
||||||
|
- **CollatorSelection** (21) - pallet_collator_selection
|
||||||
|
- **Session** (22) - pallet_session
|
||||||
|
- **Aura** (23) - pallet_aura
|
||||||
|
- **AuraExt** (24) - cumulus_pallet_aura_ext
|
||||||
|
|
||||||
|
#### XCM (Index 30-34)
|
||||||
|
- **XcmpQueue** (30) - cumulus_pallet_xcmp_queue
|
||||||
|
- **PezkuwiXcm** (31) - pallet_xcm
|
||||||
|
- **CumulusXcm** (32) - cumulus_pallet_xcm
|
||||||
|
- **MessageQueue** (34) - pallet_message_queue
|
||||||
|
|
||||||
|
#### Utilities (Index 40-44)
|
||||||
|
- **Utility** (40) - pallet_utility
|
||||||
|
- **Multisig** (41) - pallet_multisig
|
||||||
|
- **Proxy** (42) - pallet_proxy
|
||||||
|
- **Recovery** (43) - pallet_recovery
|
||||||
|
- **Vesting** (44) - pallet_vesting
|
||||||
|
|
||||||
|
#### 🔴 Identity & People (Index 50-53) - CUSTOM PALLETS
|
||||||
|
- **Identity** (50) - pallet_identity
|
||||||
|
- **IdentityKyc** (51) - pallet_identity_kyc ⭐ BENCHMARKED
|
||||||
|
- **Referral** (52) - pallet_referral ⭐ BENCHMARKED
|
||||||
|
- **Perwerde** (53) - pallet_perwerde ⭐ BENCHMARKED
|
||||||
|
|
||||||
|
#### 🔴 NFTs and Roles (Index 60-61) - CUSTOM PALLETS
|
||||||
|
- **Nfts** (60) - pallet_nfts
|
||||||
|
- **Tiki** (61) - pallet_tiki ⭐ BENCHMARKED
|
||||||
|
|
||||||
|
#### Governance (Index 70-75)
|
||||||
|
- **Council** (70) - pallet_collective::<Instance1>
|
||||||
|
- **Scheduler** (71) - pallet_scheduler
|
||||||
|
- **Democracy** (72) - pallet_democracy
|
||||||
|
- **Elections** (73) - pallet_elections_phragmen
|
||||||
|
- **Welati** (75) - pallet_welati ⭐ BENCHMARKED (PezkuwiChain Governance)
|
||||||
|
|
||||||
|
#### 🔴 Trust & Staking (Index 80-82) - CUSTOM PALLETS
|
||||||
|
- **StakingScore** (80) - pallet_staking_score ⭐ BENCHMARKED
|
||||||
|
- **Trust** (81) - pallet_trust ⭐ BENCHMARKED
|
||||||
|
- **Society** (82) - pallet_society
|
||||||
|
|
||||||
|
#### Assets & Rewards (Index 90-91)
|
||||||
|
- **Assets** (90) - pallet_assets
|
||||||
|
- **PezRewards** (91) - pallet_pez_rewards ⭐ BENCHMARKED
|
||||||
|
|
||||||
|
#### Migrations (Index 98, 248)
|
||||||
|
- **MultiBlockMigrations** (98) - pallet_migrations
|
||||||
|
- **IdentityMigrator** (248) - identity_migrator
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. People Zagros Teyrchain Runtime
|
||||||
|
|
||||||
|
**Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/runtimes/people/people-zagros/src/lib.rs`
|
||||||
|
**Spec Name:** `people-zagros`
|
||||||
|
**Spec Version:** 1_020_001
|
||||||
|
**Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### Pallets (Total: 18)
|
||||||
|
|
||||||
|
#### System Support (Index 0-4)
|
||||||
|
- **System** (0) - frame_system
|
||||||
|
- **TeyrchainSystem** (1) - cumulus_pallet_teyrchain_system
|
||||||
|
- **Timestamp** (2) - pallet_timestamp
|
||||||
|
- **TeyrchainInfo** (3) - teyrchain_info
|
||||||
|
- **WeightReclaim** (4) - cumulus_pallet_weight_reclaim
|
||||||
|
|
||||||
|
#### Monetary (Index 10-11)
|
||||||
|
- **Balances** (10) - pallet_balances
|
||||||
|
- **TransactionPayment** (11) - pallet_transaction_payment
|
||||||
|
|
||||||
|
#### Collator Support (Index 20-24)
|
||||||
|
- **Authorship** (20) - pallet_authorship
|
||||||
|
- **CollatorSelection** (21) - pallet_collator_selection
|
||||||
|
- **Session** (22) - pallet_session
|
||||||
|
- **Aura** (23) - pallet_aura
|
||||||
|
- **AuraExt** (24) - cumulus_pallet_aura_ext
|
||||||
|
|
||||||
|
#### XCM (Index 30-34)
|
||||||
|
- **XcmpQueue** (30) - cumulus_pallet_xcmp_queue
|
||||||
|
- **PezkuwiXcm** (31) - pallet_xcm
|
||||||
|
- **CumulusXcm** (32) - cumulus_pallet_xcm
|
||||||
|
- **MessageQueue** (34) - pallet_message_queue
|
||||||
|
|
||||||
|
#### Utilities (Index 40-42)
|
||||||
|
- **Utility** (40) - pallet_utility
|
||||||
|
- **Multisig** (41) - pallet_multisig
|
||||||
|
- **Proxy** (42) - pallet_proxy
|
||||||
|
|
||||||
|
#### Identity (Index 50)
|
||||||
|
- **Identity** (50) - pallet_identity
|
||||||
|
|
||||||
|
#### Migrations (Index 98, 248)
|
||||||
|
- **MultiBlockMigrations** (98) - pallet_migrations
|
||||||
|
- **IdentityMigrator** (248) - identity_migrator
|
||||||
|
|
||||||
|
**Note:** This is a minimal People Chain runtime without custom Pezkuwi pallets (no Tiki, IdentityKyc, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Penpal Test Teyrchain Runtime
|
||||||
|
|
||||||
|
**Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/runtimes/testing/penpal/src/lib.rs`
|
||||||
|
**Spec Name:** `penpal-teyrchain`
|
||||||
|
**Spec Version:** 1
|
||||||
|
**Benchmarks:** ⚠️ Limited
|
||||||
|
|
||||||
|
### Pallets (Total: 19)
|
||||||
|
|
||||||
|
- **System** (0) - frame_system
|
||||||
|
- **TeyrchainSystem** (1) - cumulus_pallet_teyrchain_system
|
||||||
|
- **Timestamp** (2) - pallet_timestamp
|
||||||
|
- **TeyrchainInfo** (3) - teyrchain_info
|
||||||
|
- **Balances** (10) - pallet_balances
|
||||||
|
- **TransactionPayment** (11) - pallet_transaction_payment
|
||||||
|
- **AssetTxPayment** (12) - pallet_asset_tx_payment
|
||||||
|
- **Authorship** (20) - pallet_authorship
|
||||||
|
- **CollatorSelection** (21) - pallet_collator_selection
|
||||||
|
- **Session** (22) - pallet_session
|
||||||
|
- **Aura** (23) - pallet_aura
|
||||||
|
- **AuraExt** (24) - cumulus_pallet_aura_ext
|
||||||
|
- **XcmpQueue** (30) - cumulus_pallet_xcmp_queue
|
||||||
|
- **PezkuwiXcm** (31) - pallet_xcm
|
||||||
|
- **CumulusXcm** (32) - cumulus_pallet_xcm
|
||||||
|
- **MessageQueue** (34) - pallet_message_queue
|
||||||
|
- **Utility** (40) - pallet_utility
|
||||||
|
- **Assets** (50) - pallet_assets::<Instance1>
|
||||||
|
- **ForeignAssets** (51) - pallet_assets::<Instance2>
|
||||||
|
- **PoolAssets** (52) - pallet_assets::<Instance3>
|
||||||
|
- **AssetConversion** (53) - pallet_asset_conversion
|
||||||
|
- **Revive** (60) - pallet_revive
|
||||||
|
- **Sudo** (255) - pallet_sudo
|
||||||
|
|
||||||
|
**Purpose:** Testing interactions between system teyrchains and non-trusted-teleporter chains.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. PezkuwiChain Test Teyrchain Runtime
|
||||||
|
|
||||||
|
**Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/runtimes/testing/pezkuwichain-teyrchain/src/lib.rs`
|
||||||
|
**Spec Name:** `test-teyrchain`
|
||||||
|
**Spec Version:** 1_020_001
|
||||||
|
**Benchmarks:** ❌ No
|
||||||
|
|
||||||
|
### Pallets (Total: 14)
|
||||||
|
|
||||||
|
- **System** - frame_system
|
||||||
|
- **Timestamp** - pallet_timestamp
|
||||||
|
- **Sudo** - pallet_sudo
|
||||||
|
- **TransactionPayment** - pallet_transaction_payment
|
||||||
|
- **WeightReclaim** - cumulus_pallet_weight_reclaim
|
||||||
|
- **TeyrchainSystem** (20) - cumulus_pallet_teyrchain_system
|
||||||
|
- **TeyrchainInfo** (21) - teyrchain_info
|
||||||
|
- **Balances** (30) - pallet_balances
|
||||||
|
- **Assets** (31) - pallet_assets
|
||||||
|
- **Aura** - pallet_aura
|
||||||
|
- **AuraExt** - cumulus_pallet_aura_ext
|
||||||
|
- **XcmpQueue** (50) - cumulus_pallet_xcmp_queue
|
||||||
|
- **PezkuwiXcm** (51) - pallet_xcm
|
||||||
|
- **CumulusXcm** (52) - cumulus_pallet_xcm
|
||||||
|
- **MessageQueue** (54) - pallet_message_queue
|
||||||
|
- **Spambot** (99) - cumulus_ping
|
||||||
|
|
||||||
|
**Purpose:** Basic test runtime for XCM and teyrchain functionality testing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Bridge Hub, Coretime, Collectives Runtimes
|
||||||
|
|
||||||
|
### Bridge Hub PezkuwiChain & Zagros
|
||||||
|
**Status:** Detected but not fully analyzed in this report.
|
||||||
|
|
||||||
|
### Coretime PezkuwiChain & Zagros
|
||||||
|
**Status:** Detected but not fully analyzed in this report.
|
||||||
|
|
||||||
|
### Collectives Zagros
|
||||||
|
**Status:** Detected but not fully analyzed in this report.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom Pallets Distribution Table
|
||||||
|
|
||||||
|
| Pallet Name | Asset Hub PZ | Asset Hub ZG | People PZ | People ZG | PZ Relay | Test Runtimes | Benchmarks |
|
||||||
|
| --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
||||||
|
| **pallet-pez-treasury** | ✓ | | | | | | ✅ |
|
||||||
|
| **pallet-presale** | ✓ | | | | | | ✅ |
|
||||||
|
| **pallet-token-wrapper** | ✓ | | | | | | ✅ |
|
||||||
|
| **pallet-identity-kyc** | | | ✓ | | | | ✅ |
|
||||||
|
| **pallet-referral** | | | ✓ | | | | ✅ |
|
||||||
|
| **pallet-perwerde** | | | ✓ | | | | ✅ |
|
||||||
|
| **pallet-tiki** | | | ✓ | | | | ✅ |
|
||||||
|
| **pallet-welati** | | | ✓ | | | | ✅ |
|
||||||
|
| **pallet-staking-score** | | | ✓ | | | | ✅ |
|
||||||
|
| **pallet-trust** | | | ✓ | | | | ✅ |
|
||||||
|
| **pallet-pez-rewards** | | | ✓ | | | | ✅ |
|
||||||
|
| **pallet-validator-pool** | | | | | ✓ | | ❌ |
|
||||||
|
| **pallet-collective-content** | | | | | | | ❌ |
|
||||||
|
| **teyrchain-info** | ✓ | ✓ | ✓ | ✓ | | ✓ | ❌ |
|
||||||
|
|
||||||
|
**Legend:**
|
||||||
|
- ✓ = Pallet is included in this runtime
|
||||||
|
- ✅ = Has benchmarks configured
|
||||||
|
- ❌ = No benchmarks
|
||||||
|
- Empty = Not included
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom Pallets Details
|
||||||
|
|
||||||
|
### 1. **pallet-pez-treasury** 💰
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/pez-treasury`
|
||||||
|
- **Runtime:** Asset Hub PezkuwiChain
|
||||||
|
- **Purpose:** PEZ token treasury management and distribution
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 2. **pallet-presale** 🎫
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/presale`
|
||||||
|
- **Runtime:** Asset Hub PezkuwiChain
|
||||||
|
- **Purpose:** Token presale management
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 3. **pallet-token-wrapper** 🔄
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/token-wrapper`
|
||||||
|
- **Runtime:** Asset Hub PezkuwiChain
|
||||||
|
- **Purpose:** Token wrapping/unwrapping functionality
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 4. **pallet-identity-kyc** 🆔
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/identity-kyc`
|
||||||
|
- **Runtime:** People PezkuwiChain
|
||||||
|
- **Purpose:** Enhanced identity with KYC capabilities
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 5. **pallet-referral** 🤝
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/referral`
|
||||||
|
- **Runtime:** People PezkuwiChain
|
||||||
|
- **Purpose:** Referral program management
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 6. **pallet-perwerde** 📚
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/perwerde`
|
||||||
|
- **Runtime:** People PezkuwiChain
|
||||||
|
- **Purpose:** Educational credentials and achievements
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 7. **pallet-tiki** 🎖️
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/tiki`
|
||||||
|
- **Runtime:** People PezkuwiChain
|
||||||
|
- **Purpose:** Role-based NFT badges system
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 8. **pallet-welati** 🏛️
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/welati`
|
||||||
|
- **Runtime:** People PezkuwiChain
|
||||||
|
- **Purpose:** PezkuwiChain governance (Serok, Parlement, Diwan)
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 9. **pallet-staking-score** 📊
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/staking-score`
|
||||||
|
- **Runtime:** People PezkuwiChain
|
||||||
|
- **Purpose:** Trust and participation scoring
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 10. **pallet-trust** 🛡️
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/trust`
|
||||||
|
- **Runtime:** People PezkuwiChain
|
||||||
|
- **Purpose:** Trust-based interactions and reputation
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 11. **pallet-pez-rewards** 🎁
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/pez-rewards`
|
||||||
|
- **Runtime:** People PezkuwiChain
|
||||||
|
- **Purpose:** PEZ token rewards distribution
|
||||||
|
- **Benchmarks:** ✅ Yes
|
||||||
|
|
||||||
|
### 12. **pallet-validator-pool** ⛏️
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/pezkuwi/pallets/validator-pool`
|
||||||
|
- **Runtime:** PezkuwiChain Relay Chain
|
||||||
|
- **Purpose:** TNPoS validator pool (shadow mode, runs parallel to NPoS)
|
||||||
|
- **Benchmarks:** ❌ No
|
||||||
|
|
||||||
|
### 13. **pallet-collective-content** 📝
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/collective-content`
|
||||||
|
- **Runtime:** None (not integrated yet)
|
||||||
|
- **Purpose:** Content management for collectives
|
||||||
|
- **Benchmarks:** ❌ No
|
||||||
|
|
||||||
|
### 14. **teyrchain-info** ℹ️
|
||||||
|
- **Location:** `/home/mamostehp/Pezkuwi-SDK/cumulus/teyrchains/pallets/teyrchain-info`
|
||||||
|
- **Runtime:** All teyrchain runtimes
|
||||||
|
- **Purpose:** Provides teyrchain ID information
|
||||||
|
- **Benchmarks:** ❌ No (infrastructure pallet)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Notes
|
||||||
|
|
||||||
|
### 🎯 Strategic Pallet Placement
|
||||||
|
|
||||||
|
1. **Asset Hub PezkuwiChain** - Economic Layer
|
||||||
|
- PEZ treasury and presale management
|
||||||
|
- Token wrapping functionality
|
||||||
|
- Full asset management suite
|
||||||
|
|
||||||
|
2. **People PezkuwiChain** - Identity & Governance Layer
|
||||||
|
- Identity + KYC integration
|
||||||
|
- Role-based NFTs (Tiki)
|
||||||
|
- Educational credentials (Perwerde)
|
||||||
|
- Governance system (Welati)
|
||||||
|
- Trust and reputation systems
|
||||||
|
- PEZ rewards distribution
|
||||||
|
|
||||||
|
3. **PezkuwiChain Relay** - Consensus Layer
|
||||||
|
- TNPoS validator pool (experimental)
|
||||||
|
- Standard NPoS staking
|
||||||
|
- Full teyrchain orchestration
|
||||||
|
|
||||||
|
### ⚠️ Missing Components
|
||||||
|
|
||||||
|
1. **Zagros Relay Chain:** Not implemented - only PezkuwiChain relay exists
|
||||||
|
2. **Bridge Hub Runtimes:** Detected but not fully analyzed
|
||||||
|
3. **Coretime Runtimes:** Detected but not fully analyzed
|
||||||
|
4. **Collectives Runtime:** Detected but not fully analyzed
|
||||||
|
|
||||||
|
### 📊 Benchmark Coverage
|
||||||
|
|
||||||
|
- **Total Custom Pallets:** 14
|
||||||
|
- **With Benchmarks:** 11 (79%)
|
||||||
|
- **Without Benchmarks:** 3 (21%)
|
||||||
|
- pallet-validator-pool (relay chain)
|
||||||
|
- pallet-collective-content (not integrated)
|
||||||
|
- teyrchain-info (infrastructure)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
|
||||||
|
### 1. Critical Issues
|
||||||
|
- ⚠️ **Zagros Runtime Missing:** Only PezkuwiChain relay chain exists. Need to decide:
|
||||||
|
- Is Zagros still planned?
|
||||||
|
- Should references be removed?
|
||||||
|
- Update terminology documentation
|
||||||
|
|
||||||
|
### 2. Benchmark Coverage
|
||||||
|
- ✅ Good coverage for production pallets (79%)
|
||||||
|
- ⚠️ Add benchmarks for `pallet-validator-pool` if going to production
|
||||||
|
- ℹ️ `teyrchain-info` and `collective-content` can stay without benchmarks
|
||||||
|
|
||||||
|
### 3. Runtime Organization
|
||||||
|
- ✅ Good separation: Economic (Asset Hub) vs Social (People Chain)
|
||||||
|
- ✅ Custom pallets well-distributed by function
|
||||||
|
- ⚠️ Consider whether all staking functionality should be on Asset Hub or if some should move
|
||||||
|
|
||||||
|
### 4. Testing
|
||||||
|
- ✅ Good test runtime coverage (Penpal, Test Teyrchain)
|
||||||
|
- ℹ️ Bridge/Coretime/Collectives runtimes need documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- **2025-12-08:** Initial comprehensive report
|
||||||
|
- **Pallets Analyzed:** 14 custom pallets across 9 runtimes
|
||||||
|
- **Benchmarks Verified:** 11/14 pallets (79%)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Generated by:** Claude Code
|
||||||
|
**Project:** Pezkuwi SDK - Independent Blockchain
|
||||||
|
**GitHub:** pezkuwichain/pezkuwi-sdk
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
[package]
|
||||||
|
name = "pezkuwi-sdk-docs"
|
||||||
|
description = "The one stop shop for developers of the pezkuwi-sdk"
|
||||||
|
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||||
|
homepage = "https://docs.pezkuwichain.io/sdk/"
|
||||||
|
repository.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
# This crate is not publish-able to crates.io for now because of docify.
|
||||||
|
publish = false
|
||||||
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Needed for all FRAME-based code
|
||||||
|
codec = { workspace = true }
|
||||||
|
frame = { features = [
|
||||||
|
"experimental",
|
||||||
|
"runtime",
|
||||||
|
], workspace = true, default-features = true }
|
||||||
|
pallet-contracts = { workspace = true }
|
||||||
|
pallet-default-config-example = { workspace = true, default-features = true }
|
||||||
|
pallet-example-offchain-worker = { workspace = true, default-features = true }
|
||||||
|
pallet-examples = { workspace = true }
|
||||||
|
scale-info = { workspace = true }
|
||||||
|
|
||||||
|
# How we build docs in rust-docs
|
||||||
|
docify = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
simple-mermaid = { workspace = true }
|
||||||
|
|
||||||
|
# Pezkuwi SDK deps, typically all should only be in scope such that we can link to their doc item.
|
||||||
|
chain-spec-builder = { workspace = true, default-features = true }
|
||||||
|
frame-benchmarking = { workspace = true }
|
||||||
|
frame-executive = { workspace = true }
|
||||||
|
frame-metadata-hash-extension = { workspace = true, default-features = true }
|
||||||
|
frame-support = { workspace = true }
|
||||||
|
frame-system = { workspace = true }
|
||||||
|
kitchensink-runtime = { workspace = true }
|
||||||
|
log = { workspace = true, default-features = true }
|
||||||
|
node-cli = { workspace = true }
|
||||||
|
pallet-example-authorization-tx-extension = { workspace = true, default-features = true }
|
||||||
|
pallet-example-single-block-migrations = { workspace = true, default-features = true }
|
||||||
|
pezkuwi-sdk = { features = [
|
||||||
|
"runtime-full",
|
||||||
|
], workspace = true, default-features = true }
|
||||||
|
subkey = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
# Substrate Client
|
||||||
|
sc-chain-spec = { workspace = true, default-features = true }
|
||||||
|
sc-cli = { workspace = true, default-features = true }
|
||||||
|
sc-client-db = { workspace = true, default-features = true }
|
||||||
|
sc-consensus-aura = { workspace = true, default-features = true }
|
||||||
|
sc-consensus-babe = { workspace = true, default-features = true }
|
||||||
|
sc-consensus-beefy = { workspace = true, default-features = true }
|
||||||
|
sc-consensus-grandpa = { workspace = true, default-features = true }
|
||||||
|
sc-consensus-manual-seal = { workspace = true, default-features = true }
|
||||||
|
sc-consensus-pow = { workspace = true, default-features = true }
|
||||||
|
sc-executor = { workspace = true, default-features = true }
|
||||||
|
sc-network = { workspace = true, default-features = true }
|
||||||
|
sc-rpc = { workspace = true, default-features = true }
|
||||||
|
sc-rpc-api = { workspace = true, default-features = true }
|
||||||
|
sc-service = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
substrate-wasm-builder = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
# Cumulus
|
||||||
|
cumulus-client-service = { workspace = true, default-features = true }
|
||||||
|
cumulus-pallet-aura-ext = { workspace = true, default-features = true }
|
||||||
|
cumulus-pallet-teyrchain-system = { workspace = true, default-features = true }
|
||||||
|
cumulus-pallet-weight-reclaim = { workspace = true, default-features = true }
|
||||||
|
cumulus-primitives-proof-size-hostfunction = { workspace = true, default-features = true }
|
||||||
|
teyrchain-info = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
# Omni Node
|
||||||
|
pezkuwi-omni-node-lib = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
# Pallets and FRAME internals
|
||||||
|
pallet-asset-conversion-tx-payment = { workspace = true, default-features = true }
|
||||||
|
pallet-asset-tx-payment = { workspace = true, default-features = true }
|
||||||
|
pallet-assets = { workspace = true, default-features = true }
|
||||||
|
pallet-aura = { workspace = true, default-features = true }
|
||||||
|
pallet-babe = { workspace = true, default-features = true }
|
||||||
|
pallet-balances = { workspace = true, default-features = true }
|
||||||
|
pallet-collective = { workspace = true, default-features = true }
|
||||||
|
pallet-democracy = { workspace = true, default-features = true }
|
||||||
|
pallet-grandpa = { workspace = true, default-features = true }
|
||||||
|
pallet-nfts = { workspace = true, default-features = true }
|
||||||
|
pallet-preimage = { workspace = true, default-features = true }
|
||||||
|
pallet-scheduler = { workspace = true, default-features = true }
|
||||||
|
pallet-skip-feeless-payment = { workspace = true, default-features = true }
|
||||||
|
pallet-timestamp = { workspace = true, default-features = true }
|
||||||
|
pallet-transaction-payment = { workspace = true, default-features = true }
|
||||||
|
pallet-uniques = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
# Primitives
|
||||||
|
sp-api = { workspace = true, default-features = true }
|
||||||
|
sp-arithmetic = { workspace = true, default-features = true }
|
||||||
|
sp-core = { workspace = true, default-features = true }
|
||||||
|
sp-genesis-builder = { workspace = true, default-features = true }
|
||||||
|
sp-io = { workspace = true, default-features = true }
|
||||||
|
sp-keyring = { workspace = true, default-features = true }
|
||||||
|
sp-offchain = { workspace = true, default-features = true }
|
||||||
|
sp-runtime = { workspace = true, default-features = true }
|
||||||
|
sp-runtime-interface = { workspace = true, default-features = true }
|
||||||
|
sp-std = { workspace = true, default-features = true }
|
||||||
|
sp-storage = { workspace = true, default-features = true }
|
||||||
|
sp-tracing = { workspace = true, default-features = true }
|
||||||
|
sp-version = { workspace = true, default-features = true }
|
||||||
|
sp-weights = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
# XCM
|
||||||
|
pallet-xcm = { workspace = true }
|
||||||
|
xcm = { workspace = true, default-features = true }
|
||||||
|
xcm-builder = { workspace = true }
|
||||||
|
xcm-docs = { workspace = true }
|
||||||
|
xcm-executor = { workspace = true }
|
||||||
|
xcm-simulator = { workspace = true }
|
||||||
|
|
||||||
|
# Runtime guides
|
||||||
|
chain-spec-guide-runtime = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
# Templates
|
||||||
|
minimal-template-runtime = { workspace = true, default-features = true }
|
||||||
|
solochain-template-runtime = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
# local packages
|
||||||
|
first-runtime = { workspace = true, default-features = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_cmd = { workspace = true }
|
||||||
|
cmd_lib = { workspace = true }
|
||||||
|
rand = { workspace = true, default-features = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
runtime-benchmarks = [
|
||||||
|
"chain-spec-builder/runtime-benchmarks",
|
||||||
|
"chain-spec-guide-runtime/runtime-benchmarks",
|
||||||
|
"cumulus-client-service/runtime-benchmarks",
|
||||||
|
"cumulus-pallet-aura-ext/runtime-benchmarks",
|
||||||
|
"cumulus-pallet-teyrchain-system/runtime-benchmarks",
|
||||||
|
"cumulus-pallet-weight-reclaim/runtime-benchmarks",
|
||||||
|
"cumulus-primitives-proof-size-hostfunction/runtime-benchmarks",
|
||||||
|
"first-runtime/runtime-benchmarks",
|
||||||
|
"frame-benchmarking/runtime-benchmarks",
|
||||||
|
"frame-executive/runtime-benchmarks",
|
||||||
|
"frame-metadata-hash-extension/runtime-benchmarks",
|
||||||
|
"frame-support/runtime-benchmarks",
|
||||||
|
"frame-system/runtime-benchmarks",
|
||||||
|
"frame/runtime-benchmarks",
|
||||||
|
"kitchensink-runtime/runtime-benchmarks",
|
||||||
|
"minimal-template-runtime/runtime-benchmarks",
|
||||||
|
"node-cli/runtime-benchmarks",
|
||||||
|
"pallet-asset-conversion-tx-payment/runtime-benchmarks",
|
||||||
|
"pallet-asset-tx-payment/runtime-benchmarks",
|
||||||
|
"pallet-assets/runtime-benchmarks",
|
||||||
|
"pallet-aura/runtime-benchmarks",
|
||||||
|
"pallet-babe/runtime-benchmarks",
|
||||||
|
"pallet-balances/runtime-benchmarks",
|
||||||
|
"pallet-collective/runtime-benchmarks",
|
||||||
|
"pallet-contracts/runtime-benchmarks",
|
||||||
|
"pallet-default-config-example/runtime-benchmarks",
|
||||||
|
"pallet-democracy/runtime-benchmarks",
|
||||||
|
"pallet-example-authorization-tx-extension/runtime-benchmarks",
|
||||||
|
"pallet-example-offchain-worker/runtime-benchmarks",
|
||||||
|
"pallet-example-single-block-migrations/runtime-benchmarks",
|
||||||
|
"pallet-examples/runtime-benchmarks",
|
||||||
|
"pallet-grandpa/runtime-benchmarks",
|
||||||
|
"pallet-nfts/runtime-benchmarks",
|
||||||
|
"pallet-preimage/runtime-benchmarks",
|
||||||
|
"pallet-scheduler/runtime-benchmarks",
|
||||||
|
"pallet-skip-feeless-payment/runtime-benchmarks",
|
||||||
|
"pallet-timestamp/runtime-benchmarks",
|
||||||
|
"pallet-transaction-payment/runtime-benchmarks",
|
||||||
|
"pallet-uniques/runtime-benchmarks",
|
||||||
|
"pallet-xcm/runtime-benchmarks",
|
||||||
|
"pezkuwi-omni-node-lib/runtime-benchmarks",
|
||||||
|
"pezkuwi-sdk/runtime-benchmarks",
|
||||||
|
"sc-chain-spec/runtime-benchmarks",
|
||||||
|
"sc-cli/runtime-benchmarks",
|
||||||
|
"sc-client-db/runtime-benchmarks",
|
||||||
|
"sc-consensus-aura/runtime-benchmarks",
|
||||||
|
"sc-consensus-babe/runtime-benchmarks",
|
||||||
|
"sc-consensus-beefy/runtime-benchmarks",
|
||||||
|
"sc-consensus-grandpa/runtime-benchmarks",
|
||||||
|
"sc-consensus-manual-seal/runtime-benchmarks",
|
||||||
|
"sc-consensus-pow/runtime-benchmarks",
|
||||||
|
"sc-executor/runtime-benchmarks",
|
||||||
|
"sc-network/runtime-benchmarks",
|
||||||
|
"sc-rpc-api/runtime-benchmarks",
|
||||||
|
"sc-rpc/runtime-benchmarks",
|
||||||
|
"sc-service/runtime-benchmarks",
|
||||||
|
"solochain-template-runtime/runtime-benchmarks",
|
||||||
|
"sp-api/runtime-benchmarks",
|
||||||
|
"sp-genesis-builder/runtime-benchmarks",
|
||||||
|
"sp-io/runtime-benchmarks",
|
||||||
|
"sp-keyring/runtime-benchmarks",
|
||||||
|
"sp-offchain/runtime-benchmarks",
|
||||||
|
"sp-runtime-interface/runtime-benchmarks",
|
||||||
|
"sp-runtime/runtime-benchmarks",
|
||||||
|
"sp-version/runtime-benchmarks",
|
||||||
|
"subkey/runtime-benchmarks",
|
||||||
|
"substrate-wasm-builder/runtime-benchmarks",
|
||||||
|
"teyrchain-info/runtime-benchmarks",
|
||||||
|
"xcm-builder/runtime-benchmarks",
|
||||||
|
"xcm-docs/runtime-benchmarks",
|
||||||
|
"xcm-executor/runtime-benchmarks",
|
||||||
|
"xcm-simulator/runtime-benchmarks",
|
||||||
|
"xcm/runtime-benchmarks",
|
||||||
|
]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<script> mermaid.init({ startOnLoad: true, theme: "dark" }, "pre.language-mermaid > code");</script>
|
||||||
|
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
<script>
|
||||||
|
function createToC() {
|
||||||
|
let sidebar = document.querySelector(".sidebar");
|
||||||
|
let headers = document.querySelectorAll("#main-content h2, #main-content h3, #main-content h4");
|
||||||
|
console.log(`detected polkadot_sdk_docs: headers: ${headers.length}`);
|
||||||
|
|
||||||
|
let toc = document.createElement("div");
|
||||||
|
toc.classList.add("sidebar-table-of-contents");
|
||||||
|
toc.appendChild(document.createElement("h2").appendChild(document.createTextNode("Table of Contents")).parentNode);
|
||||||
|
|
||||||
|
let modules = document.querySelectorAll("main .item-table a.mod");
|
||||||
|
|
||||||
|
// the first two headers are always junk
|
||||||
|
headers.forEach(header => {
|
||||||
|
let link = document.createElement("a");
|
||||||
|
link.href = "#" + header.id;
|
||||||
|
const headerTextContent = header.textContent.replace("§", "")
|
||||||
|
link.textContent = headerTextContent;
|
||||||
|
link.className = header.tagName.toLowerCase();
|
||||||
|
|
||||||
|
toc.appendChild(link);
|
||||||
|
|
||||||
|
if (header.id == "modules" && headerTextContent == "Modules") {
|
||||||
|
modules.forEach(module => {
|
||||||
|
let link = document.createElement("a");
|
||||||
|
link.href = module.href;
|
||||||
|
link.textContent = module.textContent;
|
||||||
|
link.className = "h3";
|
||||||
|
|
||||||
|
toc.appendChild(link);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// insert toc as the second child in sidebar
|
||||||
|
let sidebar_children = sidebar.children;
|
||||||
|
if (sidebar_children.length > 1) {
|
||||||
|
sidebar.insertBefore(toc, sidebar_children[1]);
|
||||||
|
} else {
|
||||||
|
sidebar.appendChild(toc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSidebarElements() {
|
||||||
|
// Create the 'Expand for More' button
|
||||||
|
var expandButton = document.createElement('button');
|
||||||
|
expandButton.innerText = 'Expand More Items';
|
||||||
|
expandButton.classList.add('expand-button');
|
||||||
|
|
||||||
|
// Insert the button at the top of the sidebar or before the '.sidebar-elems'
|
||||||
|
var sidebarElems = document.querySelector('.sidebar-elems');
|
||||||
|
sidebarElems.parentNode.insertBefore(expandButton, sidebarElems);
|
||||||
|
|
||||||
|
// Initially hide the '.sidebar-elems'
|
||||||
|
sidebarElems.style.display = 'none';
|
||||||
|
|
||||||
|
// Add click event listener to the button
|
||||||
|
expandButton.addEventListener('click', function () {
|
||||||
|
// Toggle the display of the '.sidebar-elems'
|
||||||
|
if (sidebarElems.style.display === 'none') {
|
||||||
|
sidebarElems.style.display = 'block';
|
||||||
|
expandButton.innerText = 'Collapse';
|
||||||
|
} else {
|
||||||
|
sidebarElems.style.display = 'none';
|
||||||
|
expandButton.innerText = 'Expand for More';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("DOMContentLoaded", (event) => {
|
||||||
|
// if the crate is one that starts with `polkadot_sdk_docs`
|
||||||
|
let crate_name = document.querySelector("#main-content > div > h1 > a:nth-child(1)");
|
||||||
|
if (!crate_name.textContent.startsWith("polkadot_sdk_docs")) {
|
||||||
|
console.log("skipping -- not `polkadot_sdk_docs`");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// insert class 'sdk-docs' to the body, so it enables the custom css rules.
|
||||||
|
document.body.classList.add("sdk-docs");
|
||||||
|
}
|
||||||
|
|
||||||
|
createToC();
|
||||||
|
hideSidebarElements();
|
||||||
|
|
||||||
|
console.log("updating page based on being `polkadot_sdk_docs` crate");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body.sdk-docs {
|
||||||
|
nav.side-bar {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-table-of-contents {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-table-of-contents a {
|
||||||
|
display: block;
|
||||||
|
margin: 0.2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-table-of-contents .h2 {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-table-of-contents .h3 {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-table-of-contents .h4 {
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar h2.location {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-elems {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center the 'Expand for More' button */
|
||||||
|
.expand-button {
|
||||||
|
display: inline-block;
|
||||||
|
/* Use inline-block for sizing */
|
||||||
|
margin: 10px auto;
|
||||||
|
/* Auto margins for horizontal centering */
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: auto;
|
||||||
|
/* Centering the button within its parent container */
|
||||||
|
position: relative;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
:root {
|
||||||
|
--polkadot-pink: #E6007A;
|
||||||
|
--polkadot-green: #56F39A;
|
||||||
|
--polkadot-lime: #D3FF33;
|
||||||
|
--polkadot-cyan: #00B2FF;
|
||||||
|
--polkadot-purple: #552BBF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme */
|
||||||
|
html[data-theme="light"] {
|
||||||
|
--quote-background: #f9f9f9;
|
||||||
|
--quote-border: #ccc;
|
||||||
|
--quote-text: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme */
|
||||||
|
html[data-theme="dark"] {
|
||||||
|
--quote-background: #333;
|
||||||
|
--quote-border: #555;
|
||||||
|
--quote-text: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ayu theme */
|
||||||
|
html[data-theme="ayu"] {
|
||||||
|
--quote-background: #272822;
|
||||||
|
--quote-border: #383830;
|
||||||
|
--quote-text: #f8f8f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.sdk-docs {
|
||||||
|
nav.sidebar>div.sidebar-crate>a>img {
|
||||||
|
width: 190px;
|
||||||
|
height: 52px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar {
|
||||||
|
flex: 0 0 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .sidebar-crate > .logo-container > img {
|
||||||
|
content: url("https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_Black.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom styles for blockquotes */
|
||||||
|
blockquote {
|
||||||
|
background-color: var(--quote-background);
|
||||||
|
border-left: 5px solid var(--quote-border);
|
||||||
|
color: var(--quote-text);
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 1em 1.5em;
|
||||||
|
/* font-style: italic; */
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "pezkuwi-sdk-docs-first-pallet"
|
||||||
|
description = "A simple pallet created for the pezkuwi-sdk-docs guides"
|
||||||
|
version = "0.0.0"
|
||||||
|
license = "MIT-0"
|
||||||
|
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 = { workspace = true }
|
||||||
|
docify = { workspace = true }
|
||||||
|
frame = { workspace = true, features = ["runtime"] }
|
||||||
|
scale-info = { workspace = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = ["codec/std", "frame/std", "scale-info/std"]
|
||||||
|
runtime-benchmarks = ["frame/runtime-benchmarks"]
|
||||||
@@ -0,0 +1,481 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Pallets used in the `your_first_pallet` guide.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[frame::pallet(dev_mode)]
|
||||||
|
pub mod shell_pallet {
|
||||||
|
use frame::prelude::*;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frame::pallet(dev_mode)]
|
||||||
|
pub mod pallet {
|
||||||
|
use frame::prelude::*;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
pub type Balance = u128;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
/// Single storage item, of type `Balance`.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
/// A mapping from `T::AccountId` to `Balance`
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
||||||
|
|
||||||
|
#[docify::export(impl_pallet)]
|
||||||
|
#[pallet::call]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
/// An unsafe mint that can be called by anyone. Not a great idea.
|
||||||
|
pub fn mint_unsafe(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
// ensure that this is a signed account, but we don't really check `_anyone`.
|
||||||
|
let _anyone = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
// update the balances map. Notice how all `<T: Config>` remains as `<T>`.
|
||||||
|
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
// update total issuance.
|
||||||
|
TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transfer `amount` from `origin` to `dest`.
|
||||||
|
pub fn transfer(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let sender = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
||||||
|
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||||
|
if sender_balance < amount {
|
||||||
|
return Err("InsufficientBalance".into());
|
||||||
|
}
|
||||||
|
let remainder = sender_balance - amount;
|
||||||
|
|
||||||
|
// update sender and dest balances.
|
||||||
|
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
Balances::<T>::insert(&sender, remainder);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[docify::export]
|
||||||
|
pub fn transfer_better(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let sender = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||||
|
ensure!(sender_balance >= amount, "InsufficientBalance");
|
||||||
|
let remainder = sender_balance - amount;
|
||||||
|
|
||||||
|
// .. snip
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
/// Transfer `amount` from `origin` to `dest`.
|
||||||
|
pub fn transfer_better_checked(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let sender = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||||
|
let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
|
||||||
|
|
||||||
|
// .. snip
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, doc))]
|
||||||
|
pub(crate) mod tests {
|
||||||
|
use crate::pallet::*;
|
||||||
|
|
||||||
|
#[docify::export(testing_prelude)]
|
||||||
|
use frame::testing_prelude::*;
|
||||||
|
|
||||||
|
pub(crate) const ALICE: u64 = 1;
|
||||||
|
pub(crate) const BOB: u64 = 2;
|
||||||
|
pub(crate) const CHARLIE: u64 = 3;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
// This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
|
||||||
|
// tests { .. }`
|
||||||
|
mod runtime {
|
||||||
|
use super::*;
|
||||||
|
// we need to reference our `mod pallet` as an identifier to pass to
|
||||||
|
// `construct_runtime`.
|
||||||
|
// YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
|
||||||
|
use crate::pallet as pallet_currency;
|
||||||
|
|
||||||
|
construct_runtime!(
|
||||||
|
pub enum Runtime {
|
||||||
|
// ---^^^^^^ This is where `enum Runtime` is defined.
|
||||||
|
System: frame_system,
|
||||||
|
Currency: pallet_currency,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||||
|
impl frame_system::Config for Runtime {
|
||||||
|
type Block = MockBlock<Runtime>;
|
||||||
|
// within pallet we just said `<T as frame_system::Config>::AccountId`, now we
|
||||||
|
// finally specified it.
|
||||||
|
type AccountId = u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// our simple pallet has nothing to be configured.
|
||||||
|
impl pallet_currency::Config for Runtime {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use runtime::*;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[docify::export]
|
||||||
|
fn new_test_state_basic() -> TestState {
|
||||||
|
let mut state = TestState::new_empty();
|
||||||
|
let accounts = vec![(ALICE, 100), (BOB, 100)];
|
||||||
|
state.execute_with(|| {
|
||||||
|
for (who, amount) in &accounts {
|
||||||
|
Balances::<Runtime>::insert(who, amount);
|
||||||
|
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
pub(crate) struct StateBuilder {
|
||||||
|
balances: Vec<(<Runtime as frame_system::Config>::AccountId, Balance)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export(default_state_builder)]
|
||||||
|
impl Default for StateBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { balances: vec![(ALICE, 100), (BOB, 100)] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export(impl_state_builder_add)]
|
||||||
|
impl StateBuilder {
|
||||||
|
fn add_balance(
|
||||||
|
mut self,
|
||||||
|
who: <Runtime as frame_system::Config>::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> Self {
|
||||||
|
self.balances.push((who, amount));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export(impl_state_builder_build)]
|
||||||
|
impl StateBuilder {
|
||||||
|
pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
|
||||||
|
let mut ext = TestState::new_empty();
|
||||||
|
ext.execute_with(|| {
|
||||||
|
for (who, amount) in &self.balances {
|
||||||
|
Balances::<Runtime>::insert(who, amount);
|
||||||
|
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ext.execute_with(test);
|
||||||
|
|
||||||
|
// assertions that must always hold
|
||||||
|
ext.execute_with(|| {
|
||||||
|
assert_eq!(
|
||||||
|
Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
|
||||||
|
TotalIssuance::<Runtime>::get().unwrap_or_default()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn first_test() {
|
||||||
|
TestState::new_empty().execute_with(|| {
|
||||||
|
// We expect Alice's account to have no funds.
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), None);
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), None);
|
||||||
|
|
||||||
|
// mint some funds into Alice's account.
|
||||||
|
assert_ok!(Pallet::<Runtime>::mint_unsafe(
|
||||||
|
RuntimeOrigin::signed(ALICE),
|
||||||
|
ALICE,
|
||||||
|
100
|
||||||
|
));
|
||||||
|
|
||||||
|
// re-check the above
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn state_builder_works() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn state_builder_add_balance() {
|
||||||
|
StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn state_builder_duplicate_genesis_fails() {
|
||||||
|
StateBuilder::default()
|
||||||
|
.add_balance(CHARLIE, 42)
|
||||||
|
.add_balance(CHARLIE, 43)
|
||||||
|
.build_and_execute(|| {
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn mint_works() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
// given the initial state, when:
|
||||||
|
assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
|
||||||
|
|
||||||
|
// given:
|
||||||
|
assert_ok!(Pallet::<Runtime>::mint_unsafe(
|
||||||
|
RuntimeOrigin::signed(ALICE),
|
||||||
|
CHARLIE,
|
||||||
|
100
|
||||||
|
));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn transfer_works() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
// given the initial state, when:
|
||||||
|
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
|
||||||
|
// when:
|
||||||
|
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn transfer_from_non_existent_fails() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
// given the initial state, when:
|
||||||
|
assert_err!(
|
||||||
|
Pallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
|
||||||
|
"NonExistentAccount"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then nothing has changed.
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frame::pallet(dev_mode)]
|
||||||
|
pub mod pallet_v2 {
|
||||||
|
use super::pallet::Balance;
|
||||||
|
use frame::prelude::*;
|
||||||
|
|
||||||
|
#[docify::export(config_v2)]
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {
|
||||||
|
/// The overarching event type of the runtime.
|
||||||
|
#[allow(deprecated)]
|
||||||
|
type RuntimeEvent: From<Event<Self>>
|
||||||
|
+ IsType<<Self as frame_system::Config>::RuntimeEvent>
|
||||||
|
+ TryInto<Event<Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
||||||
|
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[pallet::error]
|
||||||
|
pub enum Error<T> {
|
||||||
|
/// Account does not exist.
|
||||||
|
NonExistentAccount,
|
||||||
|
/// Account does not have enough balance.
|
||||||
|
InsufficientBalance,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[pallet::event]
|
||||||
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||||
|
pub enum Event<T: Config> {
|
||||||
|
/// A transfer succeeded.
|
||||||
|
Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::call]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[docify::export(transfer_v2)]
|
||||||
|
pub fn transfer(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let sender = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
||||||
|
let sender_balance =
|
||||||
|
Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
|
||||||
|
let remainder =
|
||||||
|
sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
|
||||||
|
|
||||||
|
Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
Balances::<T>::insert(&sender, remainder);
|
||||||
|
|
||||||
|
Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, doc))]
|
||||||
|
pub mod tests {
|
||||||
|
use super::{super::pallet::tests::StateBuilder, *};
|
||||||
|
use frame::testing_prelude::*;
|
||||||
|
const ALICE: u64 = 1;
|
||||||
|
const BOB: u64 = 2;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
pub mod runtime_v2 {
|
||||||
|
use super::*;
|
||||||
|
use crate::pallet_v2 as pallet_currency;
|
||||||
|
|
||||||
|
construct_runtime!(
|
||||||
|
pub enum Runtime {
|
||||||
|
System: frame_system,
|
||||||
|
Currency: pallet_currency,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||||
|
impl frame_system::Config for Runtime {
|
||||||
|
type Block = MockBlock<Runtime>;
|
||||||
|
type AccountId = u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl pallet_currency::Config for Runtime {
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use runtime_v2::*;
|
||||||
|
|
||||||
|
#[docify::export(transfer_works_v2)]
|
||||||
|
#[test]
|
||||||
|
fn transfer_works() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
// skip the genesis block, as events are not deposited there and we need them for
|
||||||
|
// the final assertion.
|
||||||
|
System::set_block_number(ALICE);
|
||||||
|
|
||||||
|
// given the initial state, when:
|
||||||
|
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
|
||||||
|
// now we can also check that an event has been deposited:
|
||||||
|
assert_eq!(
|
||||||
|
System::read_events_for_pallet::<Event<Runtime>>(),
|
||||||
|
vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
[package]
|
||||||
|
name = "pezkuwi-sdk-docs-first-runtime"
|
||||||
|
description = "A simple runtime created for the pezkuwi-sdk-docs guides"
|
||||||
|
version = "0.0.0"
|
||||||
|
license = "MIT-0"
|
||||||
|
authors.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
codec = { workspace = true }
|
||||||
|
scale-info = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
|
# this is a frame-based runtime, thus importing `frame` with runtime feature enabled.
|
||||||
|
frame = { workspace = true, features = ["runtime"] }
|
||||||
|
|
||||||
|
# pallets that we want to use
|
||||||
|
pallet-balances = { workspace = true }
|
||||||
|
pallet-sudo = { workspace = true }
|
||||||
|
pallet-timestamp = { workspace = true }
|
||||||
|
pallet-transaction-payment = { workspace = true }
|
||||||
|
pallet-transaction-payment-rpc-runtime-api = { workspace = true }
|
||||||
|
|
||||||
|
# other pezkuwi-sdk-deps
|
||||||
|
sp-keyring = { workspace = true }
|
||||||
|
|
||||||
|
# local pallet templates
|
||||||
|
first-pallet = { workspace = true }
|
||||||
|
|
||||||
|
docify = { workspace = true }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
substrate-wasm-builder = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = [
|
||||||
|
"codec/std",
|
||||||
|
"scale-info/std",
|
||||||
|
"serde_json/std",
|
||||||
|
|
||||||
|
"frame/std",
|
||||||
|
|
||||||
|
"pallet-balances/std",
|
||||||
|
"pallet-sudo/std",
|
||||||
|
"pallet-timestamp/std",
|
||||||
|
"pallet-transaction-payment-rpc-runtime-api/std",
|
||||||
|
"pallet-transaction-payment/std",
|
||||||
|
|
||||||
|
"first-pallet/std",
|
||||||
|
"sp-keyring/std",
|
||||||
|
|
||||||
|
"substrate-wasm-builder",
|
||||||
|
]
|
||||||
|
runtime-benchmarks = [
|
||||||
|
"first-pallet/runtime-benchmarks",
|
||||||
|
"frame/runtime-benchmarks",
|
||||||
|
"pallet-balances/runtime-benchmarks",
|
||||||
|
"pallet-sudo/runtime-benchmarks",
|
||||||
|
"pallet-timestamp/runtime-benchmarks",
|
||||||
|
"pallet-transaction-payment-rpc-runtime-api/runtime-benchmarks",
|
||||||
|
"pallet-transaction-payment/runtime-benchmarks",
|
||||||
|
"sp-keyring/runtime-benchmarks",
|
||||||
|
"substrate-wasm-builder?/runtime-benchmarks",
|
||||||
|
]
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
substrate_wasm_builder::WasmBuilder::new()
|
||||||
|
.with_current_project()
|
||||||
|
.export_heap_base()
|
||||||
|
.import_memory()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,299 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Runtime used in `your_first_runtime`.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
use alloc::{vec, vec::Vec};
|
||||||
|
use first_pallet::pallet_v2 as our_first_pallet;
|
||||||
|
use frame::{
|
||||||
|
prelude::*,
|
||||||
|
runtime::{apis, prelude::*},
|
||||||
|
};
|
||||||
|
use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, RuntimeDispatchInfo};
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[runtime_version]
|
||||||
|
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||||
|
spec_name: alloc::borrow::Cow::Borrowed("first-runtime"),
|
||||||
|
impl_name: alloc::borrow::Cow::Borrowed("first-runtime"),
|
||||||
|
authoring_version: 1,
|
||||||
|
spec_version: 0,
|
||||||
|
impl_version: 1,
|
||||||
|
apis: RUNTIME_API_VERSIONS,
|
||||||
|
transaction_version: 1,
|
||||||
|
system_version: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[docify::export(cr)]
|
||||||
|
construct_runtime!(
|
||||||
|
pub struct Runtime {
|
||||||
|
// Mandatory for all runtimes
|
||||||
|
System: frame_system,
|
||||||
|
|
||||||
|
// A number of other pallets from FRAME.
|
||||||
|
Timestamp: pallet_timestamp,
|
||||||
|
Balances: pallet_balances,
|
||||||
|
Sudo: pallet_sudo,
|
||||||
|
TransactionPayment: pallet_transaction_payment,
|
||||||
|
|
||||||
|
// Our local pallet
|
||||||
|
FirstPallet: our_first_pallet,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[docify::export_content]
|
||||||
|
mod runtime_types {
|
||||||
|
use super::*;
|
||||||
|
pub(super) type SignedExtra = (
|
||||||
|
// `frame` already provides all the signed extensions from `frame-system`. We just add the
|
||||||
|
// one related to tx-payment here.
|
||||||
|
frame::runtime::types_common::SystemTransactionExtensionsOf<Runtime>,
|
||||||
|
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||||
|
);
|
||||||
|
|
||||||
|
pub(super) type Block = frame::runtime::types_common::BlockOf<Runtime, SignedExtra>;
|
||||||
|
pub(super) type Header = HeaderFor<Runtime>;
|
||||||
|
|
||||||
|
pub(super) type RuntimeExecutive = Executive<
|
||||||
|
Runtime,
|
||||||
|
Block,
|
||||||
|
frame_system::ChainContext<Runtime>,
|
||||||
|
Runtime,
|
||||||
|
AllPalletsWithSystem,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
use runtime_types::*;
|
||||||
|
|
||||||
|
#[docify::export_content]
|
||||||
|
mod config_impls {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub const Version: RuntimeVersion = VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)]
|
||||||
|
impl frame_system::Config for Runtime {
|
||||||
|
type Block = Block;
|
||||||
|
type Version = Version;
|
||||||
|
type AccountData =
|
||||||
|
pallet_balances::AccountData<<Runtime as pallet_balances::Config>::Balance>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
||||||
|
impl pallet_balances::Config for Runtime {
|
||||||
|
type AccountStore = System;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive_impl(pallet_sudo::config_preludes::TestDefaultConfig)]
|
||||||
|
impl pallet_sudo::Config for Runtime {}
|
||||||
|
|
||||||
|
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]
|
||||||
|
impl pallet_timestamp::Config for Runtime {}
|
||||||
|
|
||||||
|
#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)]
|
||||||
|
impl pallet_transaction_payment::Config for Runtime {
|
||||||
|
type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter<Balances, ()>;
|
||||||
|
// We specify a fixed length to fee here, which essentially means all transactions charge
|
||||||
|
// exactly 1 unit of fee.
|
||||||
|
type LengthToFee = FixedFee<1, <Self as pallet_balances::Config>::Balance>;
|
||||||
|
type WeightToFee = NoFee<<Self as pallet_balances::Config>::Balance>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export(our_config_impl)]
|
||||||
|
impl our_first_pallet::Config for Runtime {
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides getters for genesis configuration presets.
|
||||||
|
pub mod genesis_config_presets {
|
||||||
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
interface::{Balance, MinimumBalance},
|
||||||
|
BalancesConfig, RuntimeGenesisConfig, SudoConfig,
|
||||||
|
};
|
||||||
|
use frame::deps::frame_support::build_struct_json_patch;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
/// Returns a development genesis config preset.
|
||||||
|
#[docify::export]
|
||||||
|
pub fn development_config_genesis() -> Value {
|
||||||
|
let endowment = <MinimumBalance as Get<Balance>>::get().max(1) * 1000;
|
||||||
|
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.
|
||||||
|
#[docify::export]
|
||||||
|
pub fn get_preset(id: &PresetId) -> Option<Vec<u8>> {
|
||||||
|
let patch = match id.as_ref() {
|
||||||
|
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.
|
||||||
|
#[docify::export]
|
||||||
|
pub fn preset_names() -> Vec<PresetId> {
|
||||||
|
vec![PresetId::from(DEV_RUNTIME_PRESET)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 apis::GenesisBuilder<Block> for Runtime {
|
||||||
|
fn build_state(config: Vec<u8>) -> GenesisBuilderResult {
|
||||||
|
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> {
|
||||||
|
crate::genesis_config_presets::preset_names()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Just a handy re-definition of some types based on what is already provided to the pallet
|
||||||
|
/// configs.
|
||||||
|
pub mod interface {
|
||||||
|
use super::Runtime;
|
||||||
|
use frame::prelude::frame_system;
|
||||||
|
|
||||||
|
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,14 @@
|
|||||||
|
//! # External Resources
|
||||||
|
//!
|
||||||
|
//! A non-exhaustive, un-opinionated list of external resources about Pezkuwi SDK.
|
||||||
|
//!
|
||||||
|
//! Unlike [`crate::guides`], or [`crate::pezkuwi_sdk::templates`] that contain material directly
|
||||||
|
//! maintained in the `pezkuwi-sdk` repository, the list of resources here are maintained by
|
||||||
|
//! third-parties, and are therefore subject to more variability. Any further resources may be added
|
||||||
|
//! by opening a pull request to the `pezkuwi-sdk` repository.
|
||||||
|
//!
|
||||||
|
//! - [Pezkuwi NFT Marketplace Tutorial by Pezkuwi Fellow Shawn Tabrizi](https://www.shawntabrizi.com/substrate-collectables-workshop/)
|
||||||
|
//! - [HEZ Code School](https://pezkuwichain.io/docs/introduction)
|
||||||
|
//! - [Pezkuwi Developers Github Organization](https://github.com/polkadot-developers/)
|
||||||
|
//! - [Pezkuwi Blockchain Academy](https://github.com/pezkuwichain/kurdistan_blockchain-akademy)
|
||||||
|
//! - [Pezkuwi Wiki](https://wiki.network.pezkuwichain.io/)
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
//! # Upgrade Teyrchain for Asynchronous Backing Compatibility
|
||||||
|
//!
|
||||||
|
//! This guide is relevant for cumulus based teyrchain projects started in 2023 or before, whose
|
||||||
|
//! backing process is synchronous where parablocks can only be built on the latest Relay Chain
|
||||||
|
//! block. Async Backing allows collators to build parablocks on older Relay Chain blocks and create
|
||||||
|
//! pipelines of multiple pending parablocks. This parallel block generation increases efficiency
|
||||||
|
//! and throughput. For more information on Async backing and its terminology, refer to the document
|
||||||
|
//! on [the Pezkuwi SDK docs.](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/async_backing_guide/index.html)
|
||||||
|
//!
|
||||||
|
//! > If starting a new teyrchain project, please use an async backing compatible template such as
|
||||||
|
//! > the
|
||||||
|
//! > [teyrchain template](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/templates/teyrchain).
|
||||||
|
//! The rollout process for Async Backing has three phases. Phases 1 and 2 below put new
|
||||||
|
//! infrastructure in place. Then we can simply turn on async backing in phase 3.
|
||||||
|
//!
|
||||||
|
//! ## Prerequisite
|
||||||
|
//!
|
||||||
|
//! The relay chain needs to have async backing enabled so double-check that the relay-chain
|
||||||
|
//! configuration contains the following three parameters (especially when testing locally e.g. with
|
||||||
|
//! zombienet):
|
||||||
|
//!
|
||||||
|
//! ```json
|
||||||
|
//! "async_backing_params": {
|
||||||
|
//! "max_candidate_depth": 3,
|
||||||
|
//! "allowed_ancestry_len": 2
|
||||||
|
//! },
|
||||||
|
//! "scheduling_lookahead": 2
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! <div class="warning"><code>scheduling_lookahead</code> must be set to 2, otherwise teyrchain
|
||||||
|
//! block times will degrade to worse than with sync backing!</div>
|
||||||
|
//!
|
||||||
|
//! ## Phase 1 - Update Teyrchain Runtime
|
||||||
|
//!
|
||||||
|
//! This phase involves configuring your teyrchain’s runtime `/runtime/src/lib.rs` to make use of
|
||||||
|
//! async backing system.
|
||||||
|
//!
|
||||||
|
//! 1. Establish and ensure constants for `capacity` and `velocity` are both set to 1 in the
|
||||||
|
//! runtime.
|
||||||
|
//! 2. Establish and ensure the constant relay chain slot duration measured in milliseconds equal to
|
||||||
|
//! `6000` in the runtime.
|
||||||
|
//! ```rust
|
||||||
|
//! // Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the
|
||||||
|
//! // relay chain.
|
||||||
|
//! pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1;
|
||||||
|
//! // How many teyrchain blocks are processed by the relay chain per parent. Limits the number of
|
||||||
|
//! // blocks authored per slot.
|
||||||
|
//! pub const BLOCK_PROCESSING_VELOCITY: u32 = 1;
|
||||||
|
//! // Relay chain slot duration, in milliseconds.
|
||||||
|
//! pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 3. Establish constants `MILLISECS_PER_BLOCK` and `SLOT_DURATION` if not already present in the
|
||||||
|
//! runtime.
|
||||||
|
//! ```ignore
|
||||||
|
//! // `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 MILLISECS_PER_BLOCK: u64 = 12000;
|
||||||
|
//! pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 4. Configure `cumulus_pallet_teyrchain_system` in the runtime.
|
||||||
|
//!
|
||||||
|
//! - Define a `FixedVelocityConsensusHook` using our capacity, velocity, and relay slot duration
|
||||||
|
//! constants. Use this to set the teyrchain system `ConsensusHook` property.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", ConsensusHook)]
|
||||||
|
//! ```ignore
|
||||||
|
//! impl cumulus_pallet_teyrchain_system::Config for Runtime {
|
||||||
|
//! ..
|
||||||
|
//! type ConsensusHook = ConsensusHook;
|
||||||
|
//! ..
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//! - Set the teyrchain system property `CheckAssociatedRelayNumber` to
|
||||||
|
//! `RelayNumberMonotonicallyIncreases`
|
||||||
|
//! ```ignore
|
||||||
|
//! impl cumulus_pallet_teyrchain_system::Config for Runtime {
|
||||||
|
//! ..
|
||||||
|
//! type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases;
|
||||||
|
//! ..
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 5. Configure `pallet_aura` in the runtime.
|
||||||
|
//!
|
||||||
|
//! - Set `AllowMultipleBlocksPerSlot` to `false` (don't worry, we will set it to `true` when we
|
||||||
|
//! activate async backing in phase 3).
|
||||||
|
//!
|
||||||
|
//! - Define `pallet_aura::SlotDuration` using our constant `SLOT_DURATION`
|
||||||
|
//! ```ignore
|
||||||
|
//! impl pallet_aura::Config for Runtime {
|
||||||
|
//! ..
|
||||||
|
//! type AllowMultipleBlocksPerSlot = ConstBool<false>;
|
||||||
|
//! #[cfg(feature = "experimental")]
|
||||||
|
//! type SlotDuration = ConstU64<SLOT_DURATION>;
|
||||||
|
//! ..
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 6. Update `sp_consensus_aura::AuraApi::slot_duration` in `sp_api::impl_runtime_apis` to match
|
||||||
|
//! the constant `SLOT_DURATION`
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/apis.rs", impl_slot_duration)]
|
||||||
|
//!
|
||||||
|
//! 7. Implement the `AuraUnincludedSegmentApi`, which allows the collator client to query its
|
||||||
|
//! runtime to determine whether it should author a block.
|
||||||
|
//!
|
||||||
|
//! - Add the dependency `cumulus-primitives-aura` to the `runtime/Cargo.toml` file for your
|
||||||
|
//! runtime
|
||||||
|
//! ```ignore
|
||||||
|
//! ..
|
||||||
|
//! cumulus-primitives-aura = { path = "../../../../primitives/aura", default-features = false }
|
||||||
|
//! ..
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! - In the same file, add `"cumulus-primitives-aura/std",` to the `std` feature.
|
||||||
|
//!
|
||||||
|
//! - Inside the `impl_runtime_apis!` block for your runtime, implement the
|
||||||
|
//! `cumulus_primitives_aura::AuraUnincludedSegmentApi` as shown below.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/apis.rs", impl_can_build_upon)]
|
||||||
|
//!
|
||||||
|
//! **Note:** With a capacity of 1 we have an effective velocity of ½ even when velocity is
|
||||||
|
//! configured to some larger value. This is because capacity will be filled after a single block is
|
||||||
|
//! produced and will only be freed up after that block is included on the relay chain, which takes
|
||||||
|
//! 2 relay blocks to accomplish. Thus with capacity 1 and velocity 1 we get the customary 12 second
|
||||||
|
//! teyrchain block time.
|
||||||
|
//!
|
||||||
|
//! 8. If your `runtime/src/lib.rs` provides a `CheckInherents` type to `register_validate_block`,
|
||||||
|
//! remove it. `FixedVelocityConsensusHook` makes it unnecessary. The following example shows how
|
||||||
|
//! `register_validate_block` should look after removing `CheckInherents`.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", register_validate_block)]
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ## Phase 2 - Update Teyrchain Nodes
|
||||||
|
//!
|
||||||
|
//! This phase consists of plugging in the new lookahead collator node.
|
||||||
|
//!
|
||||||
|
//! 1. Import `cumulus_primitives_core::ValidationCode` to `node/src/service.rs`.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", cumulus_primitives)]
|
||||||
|
//!
|
||||||
|
//! 2. In `node/src/service.rs`, modify `sc_service::spawn_tasks` to use a clone of `Backend` rather
|
||||||
|
//! than the original
|
||||||
|
//! ```ignore
|
||||||
|
//! sc_service::spawn_tasks(sc_service::SpawnTasksParams {
|
||||||
|
//! ..
|
||||||
|
//! backend: backend.clone(),
|
||||||
|
//! ..
|
||||||
|
//! })?;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 3. Add `backend` as a parameter to `start_consensus()` in `node/src/service.rs`
|
||||||
|
//! ```text
|
||||||
|
//! fn start_consensus(
|
||||||
|
//! ..
|
||||||
|
//! backend: Arc<TeyrchainBackend>,
|
||||||
|
//! ..
|
||||||
|
//! ```
|
||||||
|
//! ```ignore
|
||||||
|
//! if validator {
|
||||||
|
//! start_consensus(
|
||||||
|
//! ..
|
||||||
|
//! backend.clone(),
|
||||||
|
//! ..
|
||||||
|
//! )?;
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 4. In `node/src/service.rs` import the lookahead collator rather than the basic collator
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", lookahead_collator)]
|
||||||
|
//!
|
||||||
|
//! 5. In `start_consensus()` replace the `BasicAuraParams` struct with `AuraParams`
|
||||||
|
//! - Change the struct type from `BasicAuraParams` to `AuraParams`
|
||||||
|
//! - In the `para_client` field, pass in a cloned para client rather than the original
|
||||||
|
//! - Add a `para_backend` parameter after `para_client`, passing in our para backend
|
||||||
|
//! - Provide a `code_hash_provider` closure like that shown below
|
||||||
|
//! - Increase `authoring_duration` from 500 milliseconds to 2000
|
||||||
|
//! ```ignore
|
||||||
|
//! let params = AuraParams {
|
||||||
|
//! ..
|
||||||
|
//! para_client: client.clone(),
|
||||||
|
//! para_backend: backend.clone(),
|
||||||
|
//! ..
|
||||||
|
//! code_hash_provider: move |block_hash| {
|
||||||
|
//! client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash())
|
||||||
|
//! },
|
||||||
|
//! ..
|
||||||
|
//! authoring_duration: Duration::from_millis(2000),
|
||||||
|
//! ..
|
||||||
|
//! };
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! **Note:** Set `authoring_duration` to whatever you want, taking your own hardware into account.
|
||||||
|
//! But if the backer who should be slower than you due to reading from disk, times out at two
|
||||||
|
//! seconds your candidates will be rejected.
|
||||||
|
//!
|
||||||
|
//! 6. In `start_consensus()` replace `basic_aura::run` with `aura::run`
|
||||||
|
//! ```ignore
|
||||||
|
//! let fut =
|
||||||
|
//! aura::run::<Block, sp_consensus_aura::sr25519::AuthorityPair, _, _, _, _, _, _, _, _, _>(
|
||||||
|
//! params,
|
||||||
|
//! );
|
||||||
|
//! task_manager.spawn_essential_handle().spawn("aura", None, fut);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Phase 3 - Activate Async Backing
|
||||||
|
//!
|
||||||
|
//! This phase consists of changes to your teyrchain’s runtime that activate async backing feature.
|
||||||
|
//!
|
||||||
|
//! 1. Configure `pallet_aura`, setting `AllowMultipleBlocksPerSlot` to true in
|
||||||
|
//! `runtime/src/lib.rs`.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/configs/mod.rs", aura_config)]
|
||||||
|
//!
|
||||||
|
//! 2. Increase the maximum `UNINCLUDED_SEGMENT_CAPACITY` in `runtime/src/lib.rs`.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", async_backing_params)]
|
||||||
|
//!
|
||||||
|
//! 3. Decrease `MILLISECS_PER_BLOCK` to 6000.
|
||||||
|
//!
|
||||||
|
//! - Note: For a teyrchain which measures time in terms of its own block number rather than by
|
||||||
|
//! relay block number it may be preferable to increase velocity. Changing block time may cause
|
||||||
|
//! complications, requiring additional changes. See the section “Timing by Block Number”.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", block_times)]
|
||||||
|
//!
|
||||||
|
//! 4. Update `MAXIMUM_BLOCK_WEIGHT` to reflect the increased time available for block production.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", max_block_weight)]
|
||||||
|
//!
|
||||||
|
//! 5. Add a feature flagged alternative for `MinimumPeriod` in `pallet_timestamp`. The type should
|
||||||
|
//! be `ConstU64<0>` with the feature flag experimental, and `ConstU64<{SLOT_DURATION / 2}>`
|
||||||
|
//! without.
|
||||||
|
//! ```ignore
|
||||||
|
//! impl pallet_timestamp::Config for Runtime {
|
||||||
|
//! ..
|
||||||
|
//! #[cfg(feature = "experimental")]
|
||||||
|
//! type MinimumPeriod = ConstU64<0>;
|
||||||
|
//! #[cfg(not(feature = "experimental"))]
|
||||||
|
//! type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>;
|
||||||
|
//! ..
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Timing by Block Number
|
||||||
|
//!
|
||||||
|
//! With asynchronous backing it will be possible for teyrchains to opt for a block time of 6
|
||||||
|
//! seconds rather than 12 seconds. But modifying block duration isn’t so simple for a teyrchain
|
||||||
|
//! which was measuring time in terms of its own block number. It could result in expected and
|
||||||
|
//! actual time not matching up, stalling the teyrchain.
|
||||||
|
//!
|
||||||
|
//! One strategy to deal with this issue is to instead rely on relay chain block numbers for timing.
|
||||||
|
//! Relay block number is kept track of by each teyrchain in `pallet-teyrchain-system` with the
|
||||||
|
//! storage value `LastRelayChainBlockNumber`. This value can be obtained and used wherever timing
|
||||||
|
//! based on block number is needed.
|
||||||
|
|
||||||
|
#![deny(rustdoc::broken_intra_doc_links)]
|
||||||
|
#![deny(rustdoc::private_intra_doc_links)]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
//! # Changing Consensus
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
//! # Cumulus Enabled Teyrchain
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# DHT Bootnodes (RFC-8)
|
||||||
|
|
||||||
|
The "DHT bootnodes" mechanism, as defined in [RFC-0008: Store teyrchain bootnodes in relay chain
|
||||||
|
DHT](https://polkadot-fellows.github.io/RFCs/approved/0008-parachain-bootnodes-dht.html)
|
||||||
|
and implemented in Pezkuwi, enables teyrchain nodes to bootstrap without requiring hardcoded
|
||||||
|
bootnode addresses in the chainspec.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
This mechanism, enabled by default, allows any teyrchain node to serve as a bootnode. In each
|
||||||
|
epoch, 20 teyrchain nodes are selected as bootnodes based on the proximity of their relay chain
|
||||||
|
peer IDs to the teyrchain key for that epoch. These selected nodes register themselves in the relay
|
||||||
|
chain's Kademlia DHT as [_content providers._](
|
||||||
|
https://github.com/libp2p/specs/tree/master/kad-dht#content-provider-advertisement-and-discovery)
|
||||||
|
Other nodes can then discover and query them to obtain the multiaddresses of their teyrchain
|
||||||
|
instances.
|
||||||
|
|
||||||
|
## Information for Teyrchain Operators
|
||||||
|
|
||||||
|
The DHT bootnode mechanism simplifies teyrchain deployment by removing the need for dedicated
|
||||||
|
bootnodes and hardcoded addresses in the chainspec. It also reduces the risk of single points
|
||||||
|
of failure if predefined bootnodes become unreachable.
|
||||||
|
|
||||||
|
However, since this feature is relatively new, high-value teyrchains are still advised to include
|
||||||
|
a set of dedicated bootnodes in the chainspec as a fallback mechanism. Also, the bootnodes
|
||||||
|
specified via the `--bootnodes` command-line option are always used.
|
||||||
|
|
||||||
|
## Command-Line Options
|
||||||
|
|
||||||
|
There are two independent CLI options controlling the mechanism:
|
||||||
|
|
||||||
|
- `--no-dht-bootnode` prevents a node from acting as a DHT bootnode.
|
||||||
|
- `--no-dht-bootnode-discovery` disables discovery of other teyrchain nodes via the DHT bootnode
|
||||||
|
mechanism.
|
||||||
|
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
//! # Enable elastic scaling for a teyrchain
|
||||||
|
//!
|
||||||
|
//! <div class="warning">This guide assumes full familiarity with Asynchronous Backing and its
|
||||||
|
//! terminology, as defined in <a href="https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/async_backing_guide/index.html">the Pezkuwi SDK Docs</a>.
|
||||||
|
//! </div>
|
||||||
|
//!
|
||||||
|
//! ## Quick introduction to Elastic Scaling
|
||||||
|
//!
|
||||||
|
//! [Elastic scaling](https://www.parity.io/blog/polkadot-web3-cloud) is a feature that enables teyrchains (rollups) to use multiple cores.
|
||||||
|
//! Teyrchains can adjust their usage of core resources on the fly to increase TPS and decrease
|
||||||
|
//! latency.
|
||||||
|
//!
|
||||||
|
//! ### When do you need Elastic Scaling?
|
||||||
|
//!
|
||||||
|
//! Depending on their use case, applications might have an increased need for the following:
|
||||||
|
//! - compute (CPU weight)
|
||||||
|
//! - bandwidth (proof size)
|
||||||
|
//! - lower latency (block time)
|
||||||
|
//!
|
||||||
|
//! ### High throughput (TPS) and lower latency
|
||||||
|
//!
|
||||||
|
//! If the main bottleneck is the CPU, then your teyrchain needs to maximize the compute usage of
|
||||||
|
//! each core while also achieving a lower latency.
|
||||||
|
//! 3 cores provide the best balance between CPU, bandwidth and latency: up to 6s of execution,
|
||||||
|
//! 5MB/s of DA bandwidth and fast block time of just 2 seconds.
|
||||||
|
//!
|
||||||
|
//! ### High bandwidth
|
||||||
|
//!
|
||||||
|
//! Useful for applications that are bottlenecked by bandwidth.
|
||||||
|
//! By using 6 cores, applications can make use of up to 6s of compute, 10MB/s of bandwidth
|
||||||
|
//! while also achieving 1 second block times.
|
||||||
|
//!
|
||||||
|
//! ### Ultra low latency
|
||||||
|
//!
|
||||||
|
//! When latency is the primary requirement, Elastic scaling is currently the only solution. The
|
||||||
|
//! caveat is the efficiency of core time usage decreases as more cores are used.
|
||||||
|
//!
|
||||||
|
//! For example, using 12 cores enables fast transaction confirmations with 500ms blocks and up to
|
||||||
|
//! 20 MB/s of DA bandwidth.
|
||||||
|
//!
|
||||||
|
//! ## Dependencies
|
||||||
|
//!
|
||||||
|
//! Prerequisites: Pezkuwi-SDK `2509` or newer.
|
||||||
|
//!
|
||||||
|
//! To ensure the security and reliability of your chain when using this feature you need the
|
||||||
|
//! following:
|
||||||
|
//! - An omni-node based collator. This has already become the default choice for collators.
|
||||||
|
//! - UMP signal support.
|
||||||
|
//! [RFC103](https://github.com/polkadot-fellows/RFCs/blob/main/text/0103-introduce-core-index-commitment.md).
|
||||||
|
//! This is mandatory protection against PoV replay attacks.
|
||||||
|
//! - Enabling the relay parent offset feature. This is required to ensure the teyrchain block times
|
||||||
|
//! and transaction in-block confidence are not negatively affected by relay chain forks. Read
|
||||||
|
//! [`crate::guides::handling_teyrchain_forks`] for more information.
|
||||||
|
//! - Block production configuration adjustments.
|
||||||
|
//!
|
||||||
|
//! ### Upgrade to Pezkuwi Omni node
|
||||||
|
//!
|
||||||
|
//! Your collators need to run `pezkuwi-teyrchain` or `pezkuwi-omni-node` with the `--authoring
|
||||||
|
//! slot-based` CLI argument.
|
||||||
|
//! To avoid potential issues and get best performance it is recommeneded to always run the
|
||||||
|
//! latest release on all of the collators.
|
||||||
|
//!
|
||||||
|
//! Further information about omni-node and how to upgrade is available:
|
||||||
|
//! - [high level docs](https://docs.pezkuwichain.io/develop/toolkit/parachains/polkadot-omni-node/)
|
||||||
|
//! - [`crate::reference_docs::omni_node`]
|
||||||
|
//!
|
||||||
|
//! ### UMP signals
|
||||||
|
//!
|
||||||
|
//! UMP signals are now enabled by default in the `teyrchain-system` pallet and are used for
|
||||||
|
//! elastic scaling. You can find more technical details about UMP signals and their usage for
|
||||||
|
//! elastic scaling
|
||||||
|
//! [here](https://github.com/polkadot-fellows/RFCs/blob/main/text/0103-introduce-core-index-commitment.md).
|
||||||
|
//!
|
||||||
|
//! ### Enable the relay parent offset feature
|
||||||
|
//!
|
||||||
|
//! It is recommended to use an offset of `1`, which is sufficient to eliminate any issues
|
||||||
|
//! with relay chain forks.
|
||||||
|
//!
|
||||||
|
//! Configure the relay parent offset like this:
|
||||||
|
//! ```ignore
|
||||||
|
//! /// Build with an offset of 1 behind the relay chain best block.
|
||||||
|
//! const RELAY_PARENT_OFFSET: u32 = 1;
|
||||||
|
//!
|
||||||
|
//! impl cumulus_pallet_teyrchain_system::Config for Runtime {
|
||||||
|
//! // ...
|
||||||
|
//! type RelayParentOffset = ConstU32<RELAY_PARENT_OFFSET>;
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Implement the runtime API to retrieve the offset on the client side.
|
||||||
|
//! ```ignore
|
||||||
|
//! impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
|
||||||
|
//! fn relay_parent_offset() -> u32 {
|
||||||
|
//! RELAY_PARENT_OFFSET
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### Block production configuration
|
||||||
|
//!
|
||||||
|
//! This configuration directly controls the minimum block time and maximum number of cores
|
||||||
|
//! the teyrchain can use.
|
||||||
|
//!
|
||||||
|
//! Example configuration for a 3 core teyrchain:
|
||||||
|
//! ```ignore
|
||||||
|
//! /// The upper limit of how many teyrchain blocks are processed by the relay chain per
|
||||||
|
//! /// parent. Limits the number of blocks authored per slot. This determines the minimum
|
||||||
|
//! /// block time of the teyrchain:
|
||||||
|
//! /// `RELAY_CHAIN_SLOT_DURATION_MILLIS/BLOCK_PROCESSING_VELOCITY`
|
||||||
|
//! const BLOCK_PROCESSING_VELOCITY: u32 = 3;
|
||||||
|
//!
|
||||||
|
//! /// Maximum number of blocks simultaneously accepted by the Runtime, not yet included
|
||||||
|
//! /// into the relay chain.
|
||||||
|
//! const UNINCLUDED_SEGMENT_CAPACITY: u32 = (2 + RELAY_PARENT_OFFSET) *
|
||||||
|
//! BLOCK_PROCESSING_VELOCITY + 1;
|
||||||
|
//!
|
||||||
|
//! /// Relay chain slot duration, in milliseconds.
|
||||||
|
//! const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
|
||||||
|
//!
|
||||||
|
//! type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook<
|
||||||
|
//! Runtime,
|
||||||
|
//! RELAY_CHAIN_SLOT_DURATION_MILLIS,
|
||||||
|
//! BLOCK_PROCESSING_VELOCITY,
|
||||||
|
//! UNINCLUDED_SEGMENT_CAPACITY,
|
||||||
|
//! >;
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### Teyrchain Slot Duration
|
||||||
|
//!
|
||||||
|
//! A common source of confusion is the correct configuration of the `SlotDuration` that is passed
|
||||||
|
//! to `pallet-aura`.
|
||||||
|
//! ```ignore
|
||||||
|
//! impl pallet_aura::Config for Runtime {
|
||||||
|
//! // ...
|
||||||
|
//! type SlotDuration = ConstU64<SLOT_DURATION>;
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The slot duration determines the length of each author's turn and is decoupled from the block
|
||||||
|
//! production interval. During their slot, authors are allowed to produce multiple blocks. **The
|
||||||
|
//! slot duration is required to be at least 6s (same as on the relay chain).**
|
||||||
|
//!
|
||||||
|
//! **Configuration recommendations:**
|
||||||
|
//! - For new teyrchains starting from genesis: use a slot duration of 24 seconds
|
||||||
|
//! - For existing live teyrchains: leave the slot duration unchanged
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ## Current limitations
|
||||||
|
//!
|
||||||
|
//! ### Maximum execution time per relay chain block.
|
||||||
|
//!
|
||||||
|
//! Since teyrchain block authoring is sequential, the next block can only be built after
|
||||||
|
//! the previous one has been imported.
|
||||||
|
//! At present, a core allows up to 2 seconds of execution per relay chain block.
|
||||||
|
//!
|
||||||
|
//! If we assume a 6s teyrchain slot, and each block takes the full 2 seconds to execute,
|
||||||
|
//! the teyrchain will not be able to fully utilize the compute resources of all 3 cores.
|
||||||
|
//!
|
||||||
|
//! If the collator hardware is faster, it can author and import full blocks more quickly,
|
||||||
|
//! making it possible to utilize even more than 3 cores efficiently.
|
||||||
|
//!
|
||||||
|
//! #### Why?
|
||||||
|
//!
|
||||||
|
//! Within a 6-second teyrchain slot, collators can author multiple teyrchain blocks.
|
||||||
|
//! Before building the first block in a slot, the new block author must import the last
|
||||||
|
//! block produced by the previous author.
|
||||||
|
//! If the import of the last block is not completed before the next relay chain slot starts,
|
||||||
|
//! the new author will build on its parent (assuming it was imported). This will create a fork
|
||||||
|
//! which degrades the teyrchain block confidence and block times.
|
||||||
|
//!
|
||||||
|
//! This means that, on reference hardware, a teyrchain with a slot time of 6s can
|
||||||
|
//! effectively utilize up to 4 seconds of execution per relay chain block, because it needs to
|
||||||
|
//! ensure the next block author has enough time to import the last block.
|
||||||
|
//! Hardware with higher single-core performance can enable a teyrchain to fully utilize more
|
||||||
|
//! cores.
|
||||||
|
//!
|
||||||
|
//! ### Fixed factor scaling.
|
||||||
|
//!
|
||||||
|
//! For true elasticity, a teyrchain needs to acquire more cores when needed in an automated
|
||||||
|
//! manner. This functionality is not yet available in the SDK, thus acquiring additional
|
||||||
|
//! on-demand or bulk cores has to be managed externally.
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
//! # Enable metadata hash verification
|
||||||
|
//!
|
||||||
|
//! This guide will teach you how to enable the metadata hash verification in your runtime.
|
||||||
|
//!
|
||||||
|
//! ## What is metadata hash verification?
|
||||||
|
//!
|
||||||
|
//! Each FRAME based runtime exposes metadata about itself. This metadata is used by consumers of
|
||||||
|
//! the runtime to interpret the state, to construct transactions etc. Part of this metadata are the
|
||||||
|
//! type information. These type information can be used to e.g. decode storage entries or to decode
|
||||||
|
//! a transaction. So, the metadata is quite useful for wallets to interact with a FRAME based
|
||||||
|
//! chain. Online wallets can fetch the metadata directly from any node of the chain they are
|
||||||
|
//! connected to, but offline wallets can not do this. So, for the offline wallet to have access to
|
||||||
|
//! the metadata it needs to be transferred and stored on the device. The problem is that the
|
||||||
|
//! metadata has a size of several hundreds of kilobytes, which takes quite a while to transfer to
|
||||||
|
//! these offline wallets and the internal storage of these devices is also not big enough to store
|
||||||
|
//! the metadata for one or more networks. The next problem is that the offline wallet/user can not
|
||||||
|
//! trust the metadata to be correct. It is very important for the metadata to be correct or
|
||||||
|
//! otherwise an attacker could change them in a way that the offline wallet decodes a transaction
|
||||||
|
//! in a different way than what it will be decoded to on chain. So, the user may sign an incorrect
|
||||||
|
//! transaction leading to unexpected behavior.
|
||||||
|
//!
|
||||||
|
//! The metadata hash verification circumvents the issues of the huge metadata and the need to trust
|
||||||
|
//! some metadata blob to be correct. To generate a hash for the metadata, the metadata is chunked,
|
||||||
|
//! these chunks are put into a merkle tree and then the root of this merkle tree is the "metadata
|
||||||
|
//! hash". For a more technical explanation on how it works, see
|
||||||
|
//! [RFC78](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html). At compile
|
||||||
|
//! time the metadata hash is generated and "baked" into the runtime. This makes it extremely cheap
|
||||||
|
//! for the runtime to verify on chain that the metadata hash is correct. By having the runtime
|
||||||
|
//! verify the hash on chain, the user also doesn't need to trust the offchain metadata. If the
|
||||||
|
//! metadata hash doesn't match the on chain metadata hash the transaction will be rejected. The
|
||||||
|
//! metadata hash itself is added to the data of the transaction that is signed, this means the
|
||||||
|
//! actual hash does not appear in the transaction. On chain the same procedure is repeated with the
|
||||||
|
//! metadata hash that is known by the runtime and if the metadata hash doesn't match the signature
|
||||||
|
//! verification will fail. As the metadata hash is actually the root of a merkle tree, the offline
|
||||||
|
//! wallet can get proofs of individual types to decode a transaction. This means that the offline
|
||||||
|
//! wallet does not require the entire metadata to be present on the device.
|
||||||
|
//!
|
||||||
|
//! ## Integrating metadata hash verification into your runtime
|
||||||
|
//!
|
||||||
|
//! The integration of the metadata hash verification is split into two parts, first the actual
|
||||||
|
//! integration into the runtime and secondly the enabling of the metadata hash generation at
|
||||||
|
//! compile time.
|
||||||
|
//!
|
||||||
|
//! ### Runtime integration
|
||||||
|
//!
|
||||||
|
//! From the runtime side only the
|
||||||
|
//! [`CheckMetadataHash`](frame_metadata_hash_extension::CheckMetadataHash) needs to be added to the
|
||||||
|
//! list of signed extension:
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", template_signed_extra)]
|
||||||
|
//!
|
||||||
|
//! > **Note:**
|
||||||
|
//! >
|
||||||
|
//! > Adding the signed extension changes the encoding of the transaction and adds one extra byte
|
||||||
|
//! > per transaction!
|
||||||
|
//!
|
||||||
|
//! This signed extension will make sure to decode the requested `mode` and will add the metadata
|
||||||
|
//! hash to the signed data depending on the requested `mode`. The `mode` gives the user/wallet
|
||||||
|
//! control over deciding if the metadata hash should be verified or not. The metadata hash itself
|
||||||
|
//! is drawn from the `RUNTIME_METADATA_HASH` environment variable. If the environment variable is
|
||||||
|
//! not set, any transaction that requires the metadata hash is rejected with the error
|
||||||
|
//! `CannotLookup`. This is a security measurement to prevent including invalid transactions.
|
||||||
|
//!
|
||||||
|
//! <div class="warning">
|
||||||
|
//!
|
||||||
|
//! The extension does not work with the native runtime, because the
|
||||||
|
//! `RUNTIME_METADATA_HASH` environment variable is not set when building the
|
||||||
|
//! `frame-metadata-hash-extension` crate.
|
||||||
|
//!
|
||||||
|
//! </div>
|
||||||
|
//!
|
||||||
|
//! ### Enable metadata hash generation
|
||||||
|
//!
|
||||||
|
//! The metadata hash generation needs to be enabled when building the wasm binary. The
|
||||||
|
//! `substrate-wasm-builder` supports this out of the box:
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/build.rs", template_enable_metadata_hash)]
|
||||||
|
//!
|
||||||
|
//! > **Note:**
|
||||||
|
//! >
|
||||||
|
//! > The `metadata-hash` feature needs to be enabled for the `substrate-wasm-builder` to enable the
|
||||||
|
//! > code for being able to generate the metadata hash. It is also recommended to put the metadata
|
||||||
|
//! > hash generation behind a feature in the runtime as shown above. The reason behind is that it
|
||||||
|
//! > adds a lot of code which increases the compile time and the generation itself also increases
|
||||||
|
//! > the compile time. Thus, it is recommended to enable the feature only when the metadata hash is
|
||||||
|
//! > required (e.g. for an on-chain build).
|
||||||
|
//!
|
||||||
|
//! The two parameters to `enable_metadata_hash` are the token symbol and the number of decimals of
|
||||||
|
//! the primary token of the chain. These information are included for the wallets to show token
|
||||||
|
//! related operations in a more user friendly way.
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
//! # Enable storage weight reclaiming
|
||||||
|
//!
|
||||||
|
//! This guide will teach you how to enable storage weight reclaiming for a teyrchain. The
|
||||||
|
//! explanations in this guide assume a project structure similar to the one detailed in
|
||||||
|
//! the [substrate documentation](crate::pezkuwi_sdk::substrate#anatomy-of-a-binary-crate). Full
|
||||||
|
//! technical details are available in the original [pull request](https://github.com/paritytech/polkadot-sdk/pull/3002).
|
||||||
|
//!
|
||||||
|
//! # What is PoV reclaim?
|
||||||
|
//! When a teyrchain submits a block to a relay chain like Pezkuwi or Kusama, it sends the block
|
||||||
|
//! itself and a storage proof. Together they form the Proof-of-Validity (PoV). The PoV allows the
|
||||||
|
//! relay chain to validate the teyrchain block by re-executing it. Relay chain
|
||||||
|
//! validators distribute this PoV among themselves over the network. This distribution is costly
|
||||||
|
//! and limits the size of the storage proof. The storage weight dimension of FRAME weights reflects
|
||||||
|
//! this cost and limits the size of the storage proof. However, the storage weight determined
|
||||||
|
//! during [benchmarking](crate::reference_docs::frame_benchmarking_weight) represents the worst
|
||||||
|
//! case. In reality, runtime operations often consume less space in the storage proof. PoV reclaim
|
||||||
|
//! offers a mechanism to reclaim the difference between the benchmarked worst-case and the real
|
||||||
|
//! proof-size consumption.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! # How to enable PoV reclaim
|
||||||
|
//! ## 1. Add the host function to your node
|
||||||
|
//!
|
||||||
|
//! To reclaim excess storage weight, a teyrchain runtime needs the
|
||||||
|
//! ability to fetch the size of the storage proof from the node. The reclaim
|
||||||
|
//! mechanism uses the
|
||||||
|
//! [`storage_proof_size`](cumulus_primitives_proof_size_hostfunction::storage_proof_size)
|
||||||
|
//! host function for this purpose. For convenience, cumulus provides
|
||||||
|
//! [`TeyrchainHostFunctions`](cumulus_client_service::TeyrchainHostFunctions), a set of
|
||||||
|
//! host functions typically used by cumulus-based teyrchains. In the binary crate of your
|
||||||
|
//! teyrchain, find the instantiation of the [`WasmExecutor`](sc_executor::WasmExecutor) and set the
|
||||||
|
//! correct generic type.
|
||||||
|
//!
|
||||||
|
//! This example from the teyrchain-template shows a type definition that includes the correct
|
||||||
|
//! host functions.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", wasm_executor)]
|
||||||
|
//!
|
||||||
|
//! > **Note:**
|
||||||
|
//! >
|
||||||
|
//! > If you see error `runtime requires function imports which are not present on the host:
|
||||||
|
//! > 'env:ext_storage_proof_size_storage_proof_size_version_1'`, it is likely
|
||||||
|
//! > that this step in the guide was not set up correctly.
|
||||||
|
//!
|
||||||
|
//! ## 2. Enable storage proof recording during import
|
||||||
|
//!
|
||||||
|
//! The reclaim mechanism reads the size of the currently recorded storage proof multiple times
|
||||||
|
//! during block authoring and block import. Proof recording during authoring is already enabled on
|
||||||
|
//! teyrchains. You must also ensure that storage proof recording is enabled during block import.
|
||||||
|
//! Find where your node builds the fundamental substrate components by calling
|
||||||
|
//! [`new_full_parts`](sc_service::new_full_parts). Replace this
|
||||||
|
//! with [`new_full_parts_record_import`](sc_service::new_full_parts_record_import) and
|
||||||
|
//! pass `true` as the last parameter to enable import recording.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", component_instantiation)]
|
||||||
|
//!
|
||||||
|
//! > **Note:**
|
||||||
|
//! >
|
||||||
|
//! > If you see error `Storage root must match that calculated.` during block import, it is likely
|
||||||
|
//! > that this step in the guide was not
|
||||||
|
//! > set up correctly.
|
||||||
|
//!
|
||||||
|
//! ## 3. Add the TransactionExtension to your runtime
|
||||||
|
//!
|
||||||
|
//! In your runtime, you will find a list of TransactionExtensions.
|
||||||
|
//! To enable the reclaiming,
|
||||||
|
//! set [`StorageWeightReclaim`](cumulus_pallet_weight_reclaim::StorageWeightReclaim)
|
||||||
|
//! as a warpper of that list.
|
||||||
|
//! It is necessary that this extension wraps all the other transaction extensions in order to catch
|
||||||
|
//! the whole PoV size of the transactions.
|
||||||
|
//! The extension will check the size of the storage proof before and after an extrinsic execution.
|
||||||
|
//! It reclaims the difference between the calculated size and the benchmarked size.
|
||||||
|
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", template_signed_extra)]
|
||||||
|
//!
|
||||||
|
//! ## Optional: Verify that reclaim works
|
||||||
|
//!
|
||||||
|
//! Start your node with the log target `runtime::storage_reclaim` set to `trace` to enable full
|
||||||
|
//! logging for `StorageWeightReclaim`. The following log is an example from a local testnet. To
|
||||||
|
//! trigger the log, execute any extrinsic on the network.
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! ...
|
||||||
|
//! 2024-04-22 17:31:48.014 TRACE runtime::storage_reclaim: [ferdie] Reclaiming storage weight. benchmarked: 3593, consumed: 265 unspent: 0
|
||||||
|
//! ...
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! In the above example we see a benchmarked size of 3593 bytes, while the extrinsic only consumed
|
||||||
|
//! 265 bytes of proof size. This results in 3328 bytes of reclaim.
|
||||||
|
#![deny(rustdoc::broken_intra_doc_links)]
|
||||||
|
#![deny(rustdoc::private_intra_doc_links)]
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
//! # Teyrchain forks
|
||||||
|
//!
|
||||||
|
//! In this guide, we will examine how AURA-based teyrchains handle forks. AURA (Authority Round) is
|
||||||
|
//! a consensus mechanism where block authors rotate at fixed time intervals. Each author gets a
|
||||||
|
//! predetermined time slice during which they are allowed to author a block. On its own, this
|
||||||
|
//! mechanism is fork-free.
|
||||||
|
//!
|
||||||
|
//! However, since the relay chain provides security and serves as the source of truth for
|
||||||
|
//! teyrchains, the teyrchain is dependent on it. This relationship can introduce complexities that
|
||||||
|
//! lead to forking scenarios.
|
||||||
|
//!
|
||||||
|
//! ## Background
|
||||||
|
//! Each teyrchain block has a relay parent, which is a relay chain block that provides context to
|
||||||
|
//! our teyrchain block. The constraints the relay chain imposes on our teyrchain can cause forks
|
||||||
|
//! under certain conditions. With asynchronous-backing enabled chains, the node side is building
|
||||||
|
//! blocks on all relay chain forks. This means that no matter which fork of the relay chain
|
||||||
|
//! ultimately progressed, the teyrchain would have a block ready for that fork. The situation
|
||||||
|
//! changes when teyrchains want to produce blocks at a faster cadence. In a scenario where a
|
||||||
|
//! teyrchain might author on 3 cores with elastic scaling, it is not possible to author on all
|
||||||
|
//! relay chain forks. The time constraints do not allow it. Building on two forks would result in 6
|
||||||
|
//! blocks. The authoring of these blocks would consume more time than we have available before the
|
||||||
|
//! next relay chain block arrives. This limitation requires a more fork-resistant approach to
|
||||||
|
//! block-building.
|
||||||
|
//!
|
||||||
|
//! ## Impact of Forks
|
||||||
|
//! When a relay chain fork occurs and the teyrchain builds on a fork that will not be extended in
|
||||||
|
//! the future, the blocks built on that fork are lost and need to be rebuilt. This increases
|
||||||
|
//! latency and reduces throughput, affecting the overall performance of the teyrchain.
|
||||||
|
//!
|
||||||
|
//! # Building on Older Pelay Parents
|
||||||
|
//! Cumulus offers a way to mitigate the occurence of forks. Instead of picking a block at the tip
|
||||||
|
//! of the relay chain to build blocks, the node side can pick a relay chain block that is older. By
|
||||||
|
//! building on 12s old relay chain blocks, forks will already have settled and the teyrchain can
|
||||||
|
//! build fork-free.
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! Without offset:
|
||||||
|
//! Relay Chain: A --- B --- C --- D --- E
|
||||||
|
//! \
|
||||||
|
//! --- D' --- E'
|
||||||
|
//! Teyrchain: X --- Y --- ? (builds on both D and D', wasting resources)
|
||||||
|
//!
|
||||||
|
//! With offset (2 blocks):
|
||||||
|
//! Relay Chain: A --- B --- C --- D --- E
|
||||||
|
//! \
|
||||||
|
//! --- D' --- E'
|
||||||
|
//! Teyrchain: X(A) - Y (B) - Z (on C, fork already resolved)
|
||||||
|
//! ```
|
||||||
|
//! **Note:** It is possible that relay chain forks extend over more than 1-2 blocks. However, it is
|
||||||
|
//! unlikely.
|
||||||
|
//! ## Tradeoffs
|
||||||
|
//! Fork-free teyrchains come with a few tradeoffs:
|
||||||
|
//! - The latency of incoming XCM messages will be delayed by `N * 6s`, where `N` is the number of
|
||||||
|
//! relay chain blocks we want to offset by. For example, by building 2 relay chain blocks behind
|
||||||
|
//! the tip, the XCM latency will be increased by 12 seconds.
|
||||||
|
//! - The available PoV space will be slightly reduced. Assuming a 10mb PoV, teyrchains need to be
|
||||||
|
//! ready to sacrifice around 0.5% of PoV space.
|
||||||
|
//!
|
||||||
|
//! ## Enabling Guide
|
||||||
|
//! The decision whether the teyrchain should build on older relay parents is embedded into the
|
||||||
|
//! runtime. After the changes are implemented, the runtime will enforce that no author can build
|
||||||
|
//! with an offset smaller than the desired offset. If you wish to keep your current teyrchain
|
||||||
|
//! behaviour and do not want aforementioned tradeoffs, set the offset to 0.
|
||||||
|
//!
|
||||||
|
//! **Note:** The APIs mentioned here are available in `pezkuwi-sdk` versions after `stable-2506`.
|
||||||
|
//!
|
||||||
|
//! 1. Define the relay parent offset your teyrchain should respect in the runtime.
|
||||||
|
//! ```ignore
|
||||||
|
//! const RELAY_PARENT_OFFSET = 2;
|
||||||
|
//! ```
|
||||||
|
//! 2. Pass this constant to the `teyrchain-system` pallet.
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! impl cumulus_pallet_teyrchain_system::Config for Runtime {
|
||||||
|
//! // Other config items here
|
||||||
|
//! ...
|
||||||
|
//! type RelayParentOffset = ConstU32<RELAY_PARENT_OFFSET>;
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//! 3. Implement the `RelayParentOffsetApi` runtime API for your runtime.
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
|
||||||
|
//! fn relay_parent_offset() -> u32 {
|
||||||
|
//! RELAY_PARENT_OFFSET
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//! 4. Increase the `UNINCLUDED_SEGMENT_CAPICITY` for your runtime. It needs to be increased by
|
||||||
|
//! `RELAY_PARENT_OFFSET * BLOCK_PROCESSING_VELOCITY`.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//! # Pezkuwi SDK Docs Guides
|
||||||
|
//!
|
||||||
|
//! This crate contains a collection of guides that are foundational to the developers of
|
||||||
|
//! Pezkuwi SDK. They are common user-journeys that are traversed in the Pezkuwi ecosystem.
|
||||||
|
//!
|
||||||
|
//! The main user-journey covered by these guides is:
|
||||||
|
//!
|
||||||
|
//! * [`your_first_pallet`], where you learn what a FRAME pallet is, and write your first
|
||||||
|
//! application logic.
|
||||||
|
//! * [`your_first_runtime`], where you learn how to compile your pallets into a WASM runtime.
|
||||||
|
//! * [`your_first_node`], where you learn how to run the said runtime in a node.
|
||||||
|
//!
|
||||||
|
//! > By this step, you have already launched a full Pezkuwi-SDK-based blockchain!
|
||||||
|
//!
|
||||||
|
//! Once done, feel free to step up into one of our templates: [`crate::pezkuwi_sdk::templates`].
|
||||||
|
//!
|
||||||
|
//! [`your_first_pallet`]: crate::guides::your_first_pallet
|
||||||
|
//! [`your_first_runtime`]: crate::guides::your_first_runtime
|
||||||
|
//! [`your_first_node`]: crate::guides::your_first_node
|
||||||
|
//!
|
||||||
|
//! Other guides are related to other miscellaneous topics and are listed as modules below.
|
||||||
|
|
||||||
|
/// Write your first simple pallet, learning the most most basic features of FRAME along the way.
|
||||||
|
pub mod your_first_pallet;
|
||||||
|
|
||||||
|
/// Write your first real [runtime](`crate::reference_docs::wasm_meta_protocol`),
|
||||||
|
/// compiling it to [WASM](crate::pezkuwi_sdk::substrate#wasm-build).
|
||||||
|
pub mod your_first_runtime;
|
||||||
|
|
||||||
|
/// Running the given runtime with a node. No specific consensus mechanism is used at this stage.
|
||||||
|
pub mod your_first_node;
|
||||||
|
|
||||||
|
/// How to enhance a given runtime and node to be cumulus-enabled, run it as a teyrchain
|
||||||
|
/// and connect it to a relay-chain.
|
||||||
|
// pub mod your_first_teyrchain;
|
||||||
|
|
||||||
|
/// How to enable storage weight reclaiming in a teyrchain node and runtime.
|
||||||
|
pub mod enable_pov_reclaim;
|
||||||
|
|
||||||
|
/// How to enable Async Backing on teyrchain projects that started in 2023 or before.
|
||||||
|
pub mod async_backing_guide;
|
||||||
|
|
||||||
|
/// How to enable metadata hash verification in the runtime.
|
||||||
|
pub mod enable_metadata_hash;
|
||||||
|
|
||||||
|
/// How to enable elastic scaling on a teyrchain.
|
||||||
|
pub mod enable_elastic_scaling;
|
||||||
|
|
||||||
|
/// How to parameterize teyrchain forking in relation to relay chain forking.
|
||||||
|
pub mod handling_teyrchain_forks;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
//! # XCM Enabled Teyrchain
|
||||||
@@ -0,0 +1,342 @@
|
|||||||
|
//! # Your first Node
|
||||||
|
//!
|
||||||
|
//! In this guide, you will learn how to run a runtime, such as the one created in
|
||||||
|
//! [`your_first_runtime`], in a node. Within the context of this guide, we will focus on running
|
||||||
|
//! the runtime with an [`omni-node`]. Please first read this page to learn about the OmniNode, and
|
||||||
|
//! other options when it comes to running a node.
|
||||||
|
//!
|
||||||
|
//! [`your_first_runtime`] is a runtime with no consensus related code, and therefore can only be
|
||||||
|
//! executed with a node that also expects no consensus ([`sc_consensus_manual_seal`]).
|
||||||
|
//! `pezkuwi-omni-node`'s [`--dev-block-time`] precisely does this.
|
||||||
|
//!
|
||||||
|
//! > All of the following steps are coded as unit tests of this module. Please see `Source` of the
|
||||||
|
//! > page for more information.
|
||||||
|
//!
|
||||||
|
//! ## Running The Omni Node
|
||||||
|
//!
|
||||||
|
//! ### Installs
|
||||||
|
//!
|
||||||
|
//! The `pezkuwi-omni-node` can either be downloaded from the latest [Release](https://github.com/pezkuwichain/pezkuwi-sdk/releases/) of `pezkuwi-sdk`,
|
||||||
|
//! or installed using `cargo`:
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! cargo install pezkuwi-omni-node
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Next, we need to install the `chain-spec-builder`. This is the tool that allows us to build
|
||||||
|
//! chain-specifications, through interacting with the genesis related APIs of the runtime, as
|
||||||
|
//! described in [`crate::guides::your_first_runtime#genesis-configuration`].
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! cargo install staging-chain-spec-builder
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! > The name of the crate is prefixed with `staging` as the crate name `chain-spec-builder` on
|
||||||
|
//! > crates.io is already taken and is not controlled by `pezkuwi-sdk` developers.
|
||||||
|
//!
|
||||||
|
//! ### Building Runtime
|
||||||
|
//!
|
||||||
|
//! Next, we need to build the corresponding runtime that we wish to interact with.
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! cargo build --release -p path-to-runtime
|
||||||
|
//! ```
|
||||||
|
//! Equivalent code in tests:
|
||||||
|
#![doc = docify::embed!("./src/guides/your_first_runtime.rs", build_runtime)]
|
||||||
|
//!
|
||||||
|
//! This creates the wasm file under `./target/{release}/wbuild/release` directory.
|
||||||
|
//!
|
||||||
|
//! ### Building Chain Spec
|
||||||
|
//!
|
||||||
|
//! Next, we can generate the corresponding chain-spec file. For this example, we will use the
|
||||||
|
//! `development` (`sp_genesis_config::DEVELOPMENT`) preset.
|
||||||
|
//!
|
||||||
|
//! Note that we intend to run this chain-spec with `pezkuwi-omni-node`, which is tailored for
|
||||||
|
//! running teyrchains. This requires the chain-spec to always contain the `para_id` and a
|
||||||
|
//! `relay_chain` fields, which are provided below as CLI arguments.
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! chain-spec-builder \
|
||||||
|
//! -c <path-to-output> \
|
||||||
|
//! create \
|
||||||
|
//! --relay-chain dontcare \
|
||||||
|
//! --runtime pezkuwi_sdk_docs_first_runtime.wasm \
|
||||||
|
//! named-preset development
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Equivalent code in tests:
|
||||||
|
#![doc = docify::embed!("./src/guides/your_first_node.rs", csb)]
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ### Running `pezkuwi-omni-node`
|
||||||
|
//!
|
||||||
|
//! Finally, we can run the node with the generated chain-spec file. We can also specify the block
|
||||||
|
//! time using the `--dev-block-time` flag.
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! pezkuwi-omni-node \
|
||||||
|
//! --tmp \
|
||||||
|
//! --dev-block-time 1000 \
|
||||||
|
//! --chain <chain_spec_file>.json
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! > Note that we always prefer to use `--tmp` for testing, as it will save the chain state to a
|
||||||
|
//! > temporary folder, allowing the chain-to be easily restarted without `purge-chain`. See
|
||||||
|
//! > [`sc_cli::commands::PurgeChainCmd`] and [`sc_cli::commands::RunCmd::tmp`] for more info.
|
||||||
|
//!
|
||||||
|
//! This will start the node and import the blocks. Note while using `--dev-block-time`, the node
|
||||||
|
//! will use the testing-specific manual-seal consensus. This is an efficient way to test the
|
||||||
|
//! application logic of your runtime, without needing to yet care about consensus, block
|
||||||
|
//! production, relay-chain and so on.
|
||||||
|
//!
|
||||||
|
//! ### Next Steps
|
||||||
|
//!
|
||||||
|
//! * See the rest of the steps in [`crate::reference_docs::omni_node#user-journey`].
|
||||||
|
//!
|
||||||
|
//! [`runtime`]: crate::reference_docs::glossary#runtime
|
||||||
|
//! [`node`]: crate::reference_docs::glossary#node
|
||||||
|
//! [`build_config`]: first_runtime::Runtime#method.build_config
|
||||||
|
//! [`omni-node`]: crate::reference_docs::omni_node
|
||||||
|
//! [`--dev-block-time`]: (pezkuwi_omni_node_lib::cli::Cli::dev_block_time)
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use assert_cmd::assert::OutputAssertExt;
|
||||||
|
use cmd_lib::*;
|
||||||
|
use rand::Rng;
|
||||||
|
use sc_chain_spec::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET};
|
||||||
|
use sp_genesis_builder::PresetId;
|
||||||
|
use std::{
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
path::PathBuf,
|
||||||
|
process::{ChildStderr, Command, Stdio},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
const PARA_RUNTIME: &'static str = "teyrchain-template-runtime";
|
||||||
|
const CHAIN_SPEC_BUILDER: &'static str = "chain-spec-builder";
|
||||||
|
const OMNI_NODE: &'static str = "pezkuwi-omni-node";
|
||||||
|
|
||||||
|
fn cargo() -> Command {
|
||||||
|
Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_target_directory() -> Option<PathBuf> {
|
||||||
|
let output = cargo().arg("metadata").arg("--format-version=1").output().ok()?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let metadata: serde_json::Value = serde_json::from_slice(&output.stdout).ok()?;
|
||||||
|
let target_directory = metadata["target_directory"].as_str()?;
|
||||||
|
|
||||||
|
Some(PathBuf::from(target_directory))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_release_binary(name: &str) -> Option<PathBuf> {
|
||||||
|
let target_dir = get_target_directory()?;
|
||||||
|
let release_path = target_dir.join("release").join(name);
|
||||||
|
|
||||||
|
if release_path.exists() {
|
||||||
|
Some(release_path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_wasm(runtime_name: &str) -> Option<PathBuf> {
|
||||||
|
let target_dir = get_target_directory()?;
|
||||||
|
let wasm_path = target_dir
|
||||||
|
.join("release")
|
||||||
|
.join("wbuild")
|
||||||
|
.join(runtime_name)
|
||||||
|
.join(format!("{}.wasm", runtime_name.replace('-', "_")));
|
||||||
|
|
||||||
|
if wasm_path.exists() {
|
||||||
|
Some(wasm_path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_build_runtimes() {
|
||||||
|
if find_wasm(&PARA_RUNTIME).is_none() {
|
||||||
|
println!("Building teyrchain-template-runtime...");
|
||||||
|
Command::new("cargo")
|
||||||
|
.arg("build")
|
||||||
|
.arg("--release")
|
||||||
|
.arg("-p")
|
||||||
|
.arg(PARA_RUNTIME)
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(find_wasm(PARA_RUNTIME).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_build_chain_spec_builder() {
|
||||||
|
if find_release_binary(CHAIN_SPEC_BUILDER).is_none() {
|
||||||
|
println!("Building chain-spec-builder...");
|
||||||
|
Command::new("cargo")
|
||||||
|
.arg("build")
|
||||||
|
.arg("--release")
|
||||||
|
.arg("-p")
|
||||||
|
.arg("staging-chain-spec-builder")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
}
|
||||||
|
assert!(find_release_binary(CHAIN_SPEC_BUILDER).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_build_omni_node() {
|
||||||
|
if find_release_binary(OMNI_NODE).is_none() {
|
||||||
|
println!("Building pezkuwi-omni-node...");
|
||||||
|
Command::new("cargo")
|
||||||
|
.arg("build")
|
||||||
|
.arg("--release")
|
||||||
|
.arg("-p")
|
||||||
|
.arg("pezkuwi-omni-node")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn imported_block_found(stderr: ChildStderr, block: u64, timeout: u64) -> bool {
|
||||||
|
tokio::time::timeout(Duration::from_secs(timeout), async {
|
||||||
|
let want = format!("Imported #{}", block);
|
||||||
|
let reader = BufReader::new(stderr);
|
||||||
|
let mut found_block = false;
|
||||||
|
for line in reader.lines() {
|
||||||
|
if line.unwrap().contains(&want) {
|
||||||
|
found_block = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found_block
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn test_runtime_preset(
|
||||||
|
runtime: &'static str,
|
||||||
|
block_time: u64,
|
||||||
|
maybe_preset: Option<PresetId>,
|
||||||
|
) {
|
||||||
|
sp_tracing::try_init_simple();
|
||||||
|
maybe_build_runtimes();
|
||||||
|
maybe_build_chain_spec_builder();
|
||||||
|
maybe_build_omni_node();
|
||||||
|
|
||||||
|
let chain_spec_builder =
|
||||||
|
find_release_binary(&CHAIN_SPEC_BUILDER).expect("we built it above; qed");
|
||||||
|
let omni_node = find_release_binary(OMNI_NODE).expect("we built it above; qed");
|
||||||
|
let runtime_path = find_wasm(runtime).expect("we built it above; qed");
|
||||||
|
|
||||||
|
let random_seed: u32 = rand::thread_rng().gen();
|
||||||
|
let chain_spec_file = std::env::current_dir()
|
||||||
|
.unwrap()
|
||||||
|
.join(format!("{}_{}_{}.json", runtime, block_time, random_seed));
|
||||||
|
|
||||||
|
Command::new(chain_spec_builder)
|
||||||
|
.args(["-c", chain_spec_file.to_str().unwrap()])
|
||||||
|
.arg("create")
|
||||||
|
.args(["--relay-chain", "dontcare"])
|
||||||
|
.args(["-r", runtime_path.to_str().unwrap()])
|
||||||
|
.args(match maybe_preset {
|
||||||
|
Some(preset) => vec!["named-preset".to_string(), preset.to_string()],
|
||||||
|
None => vec!["default".to_string()],
|
||||||
|
})
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
let mut child = Command::new(omni_node)
|
||||||
|
.arg("--tmp")
|
||||||
|
.args(["--chain", chain_spec_file.to_str().unwrap()])
|
||||||
|
.args(["--dev-block-time", block_time.to_string().as_str()])
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Take stderr and parse it with timeout.
|
||||||
|
let stderr = child.stderr.take().unwrap();
|
||||||
|
let expected_blocks = (10_000 / block_time).saturating_div(2);
|
||||||
|
assert!(expected_blocks > 0, "test configuration is bad, should give it more time");
|
||||||
|
assert_eq!(imported_block_found(stderr, expected_blocks, 100).await, true);
|
||||||
|
std::fs::remove_file(chain_spec_file).unwrap();
|
||||||
|
child.kill().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets up omni-node to run a text exercise based on a chain spec.
|
||||||
|
async fn omni_node_test_setup(chain_spec_path: PathBuf) {
|
||||||
|
maybe_build_omni_node();
|
||||||
|
let omni_node = find_release_binary(OMNI_NODE).unwrap();
|
||||||
|
|
||||||
|
let mut child = Command::new(omni_node)
|
||||||
|
.arg("--dev")
|
||||||
|
.args(["--chain", chain_spec_path.to_str().unwrap()])
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stderr = child.stderr.take().unwrap();
|
||||||
|
assert_eq!(imported_block_found(stderr, 7, 100).await, true);
|
||||||
|
child.kill().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn works_with_different_block_times() {
|
||||||
|
test_runtime_preset(PARA_RUNTIME, 100, Some(DEV_RUNTIME_PRESET.into())).await;
|
||||||
|
test_runtime_preset(PARA_RUNTIME, 3000, Some(DEV_RUNTIME_PRESET.into())).await;
|
||||||
|
|
||||||
|
// we need this snippet just for docs
|
||||||
|
#[docify::export_content(csb)]
|
||||||
|
fn build_teyrchain_spec_works() {
|
||||||
|
let chain_spec_builder = find_release_binary(&CHAIN_SPEC_BUILDER).unwrap();
|
||||||
|
let runtime_path = find_wasm(PARA_RUNTIME).unwrap();
|
||||||
|
let output = "/tmp/demo-chain-spec.json";
|
||||||
|
let runtime_str = runtime_path.to_str().unwrap();
|
||||||
|
run_cmd!(
|
||||||
|
$chain_spec_builder -c $output create --relay-chain dontcare -r $runtime_str named-preset development
|
||||||
|
).expect("Failed to run command");
|
||||||
|
std::fs::remove_file(output).unwrap();
|
||||||
|
}
|
||||||
|
build_teyrchain_spec_works();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn teyrchain_runtime_works() {
|
||||||
|
// TODO: None doesn't work. But maybe it should? it would be misleading as many users might
|
||||||
|
// use it.
|
||||||
|
for preset in [Some(DEV_RUNTIME_PRESET.into()), Some(LOCAL_TESTNET_RUNTIME_PRESET.into())] {
|
||||||
|
test_runtime_preset(PARA_RUNTIME, 1000, preset).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn omni_node_dev_mode_works() {
|
||||||
|
//Omni Node in dev mode works with teyrchain's template `dev_chain_spec`
|
||||||
|
let dev_chain_spec = std::env::current_dir()
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("templates")
|
||||||
|
.join("teyrchain")
|
||||||
|
.join("dev_chain_spec.json");
|
||||||
|
omni_node_test_setup(dev_chain_spec).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
// This is a regresion test so that we still remain compatible with runtimes that use
|
||||||
|
// `para-id` in chain specs, instead of implementing the
|
||||||
|
// `cumulus_primitives_core::GetTeyrchainInfo`.
|
||||||
|
async fn omni_node_dev_mode_works_without_getteyrchaininfo() {
|
||||||
|
let dev_chain_spec = std::env::current_dir()
|
||||||
|
.unwrap()
|
||||||
|
.join("src/guides/teyrchain_without_getteyrchaininfo.json");
|
||||||
|
omni_node_test_setup(dev_chain_spec).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,789 @@
|
|||||||
|
//! # Currency Pallet
|
||||||
|
//!
|
||||||
|
//! By the end of this guide, you will have written a small FRAME pallet (see
|
||||||
|
//! [`crate::pezkuwi_sdk::frame_runtime`]) that is capable of handling a simple crypto-currency.
|
||||||
|
//! This pallet will:
|
||||||
|
//!
|
||||||
|
//! 1. Allow anyone to mint new tokens into accounts (which is obviously not a great idea for a real
|
||||||
|
//! system).
|
||||||
|
//! 2. Allow any user that owns tokens to transfer them to others.
|
||||||
|
//! 3. Track the total issuance of all tokens at all times.
|
||||||
|
//!
|
||||||
|
//! > This guide will build a currency pallet from scratch using only the lowest primitives of
|
||||||
|
//! > FRAME, and is mainly intended for education, not *applicability*. For example, almost all
|
||||||
|
//! > FRAME-based runtimes use various techniques to re-use a currency pallet instead of writing
|
||||||
|
//! > one. Further advanced FRAME related topics are discussed in [`crate::reference_docs`].
|
||||||
|
//!
|
||||||
|
//! ## Writing Your First Pallet
|
||||||
|
//!
|
||||||
|
//! To get started, clone one of the templates mentioned in [`crate::pezkuwi_sdk::templates`]. We
|
||||||
|
//! recommend using the `pezkuwi-sdk-minimal-template`. You might need to change small parts of
|
||||||
|
//! this guide, namely the crate/package names, based on which template you use.
|
||||||
|
//!
|
||||||
|
//! > Be aware that you can read the entire source code backing this tutorial by clicking on the
|
||||||
|
//! > `source` button at the top right of the page.
|
||||||
|
//!
|
||||||
|
//! You should have studied the following modules as a prelude to this guide:
|
||||||
|
//!
|
||||||
|
//! - [`crate::reference_docs::blockchain_state_machines`]
|
||||||
|
//! - [`crate::reference_docs::trait_based_programming`]
|
||||||
|
//! - [`crate::pezkuwi_sdk::frame_runtime`]
|
||||||
|
//!
|
||||||
|
//! ## Topics Covered
|
||||||
|
//!
|
||||||
|
//! The following FRAME topics are covered in this guide:
|
||||||
|
//!
|
||||||
|
//! - [`pallet::storage`]
|
||||||
|
//! - [`pallet::call`]
|
||||||
|
//! - [`pallet::event`]
|
||||||
|
//! - [`pallet::error`]
|
||||||
|
//! - Basics of testing a pallet
|
||||||
|
//! - [Constructing a runtime](frame::runtime::prelude::construct_runtime)
|
||||||
|
//!
|
||||||
|
//! ### Shell Pallet
|
||||||
|
//!
|
||||||
|
//! Consider the following as a "shell pallet". We continue building the rest of this pallet based
|
||||||
|
//! on this template.
|
||||||
|
//!
|
||||||
|
//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any
|
||||||
|
//! pallet. Refer to the documentation of each to get an overview of what they do.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", shell_pallet)]
|
||||||
|
//!
|
||||||
|
//! All of the code that follows in this guide should live inside of the `mod pallet`.
|
||||||
|
//!
|
||||||
|
//! ### Storage
|
||||||
|
//!
|
||||||
|
//! First, we will need to create two onchain storage declarations.
|
||||||
|
//!
|
||||||
|
//! One should be a mapping from account-ids to a balance type, and one value that is the total
|
||||||
|
//! issuance.
|
||||||
|
//!
|
||||||
|
//! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of
|
||||||
|
//! > simplicity, we are hardcoding this type. In a real pallet is best practice to define it as a
|
||||||
|
//! > generic bounded type in the `Config` trait, and then specify it in the implementation.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balance)]
|
||||||
|
//!
|
||||||
|
//! The definition of these two storage items, based on [`pallet::storage`] details, is as follows:
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", TotalIssuance)]
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balances)]
|
||||||
|
//!
|
||||||
|
//! ### Dispatchables
|
||||||
|
//!
|
||||||
|
//! Next, we will define the dispatchable functions. As per [`pallet::call`], these will be defined
|
||||||
|
//! as normal `fn`s attached to `struct Pallet`.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_pallet)]
|
||||||
|
//!
|
||||||
|
//! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related
|
||||||
|
//! details:
|
||||||
|
//!
|
||||||
|
//! - Where do `T::AccountId` and `T::RuntimeOrigin` come from? These are both defined in
|
||||||
|
//! [`frame::prelude::frame_system::Config`], therefore we can access them in `T`.
|
||||||
|
//! - What is `ensure_signed`, and what does it do with the aforementioned `T::RuntimeOrigin`? This
|
||||||
|
//! is outside the scope of this guide, and you can learn more about it in the origin reference
|
||||||
|
//! document ([`crate::reference_docs::frame_origin`]). For now, you should only know the
|
||||||
|
//! signature of the function: it takes a generic `T::RuntimeOrigin` and returns a
|
||||||
|
//! `Result<T::AccountId, _>`. So by the end of this function call, we know that this dispatchable
|
||||||
|
//! was signed by `sender`.
|
||||||
|
#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", ensure_signed)]
|
||||||
|
//!
|
||||||
|
//! - Where does `mutate`, `get` and `insert` and other storage APIs come from? All of them are
|
||||||
|
//! explained in the corresponding `type`, for example, for `Balances::<T>::insert`, you can look
|
||||||
|
//! into [`frame::prelude::StorageMap::insert`].
|
||||||
|
//!
|
||||||
|
//! - The return type of all dispatchable functions is [`frame::prelude::DispatchResult`]:
|
||||||
|
#![doc = docify::embed!("../../substrate/frame/support/src/dispatch.rs", DispatchResult)]
|
||||||
|
//!
|
||||||
|
//! Which is more or less a normal Rust `Result`, with a custom [`frame::prelude::DispatchError`] as
|
||||||
|
//! the `Err` variant. We won't cover this error in detail here, but importantly you should know
|
||||||
|
//! that there is an `impl From<&'static string> for DispatchError` provided (see
|
||||||
|
//! [here](`frame::prelude::DispatchError#impl-From<%26str>-for-DispatchError`)). Therefore,
|
||||||
|
//! we can use basic string literals as our error type and `.into()` them into `DispatchError`.
|
||||||
|
//!
|
||||||
|
//! - Why are all `get` and `mutate` functions returning an `Option`? This is the default behavior
|
||||||
|
//! of FRAME storage APIs. You can learn more about how to override this by looking into
|
||||||
|
//! [`pallet::storage`], and [`frame::prelude::ValueQuery`]/[`frame::prelude::OptionQuery`]
|
||||||
|
//!
|
||||||
|
//! ### Improving Errors
|
||||||
|
//!
|
||||||
|
//! How we handle error in the above snippets is fairly rudimentary. Let's look at how this can be
|
||||||
|
//! improved. First, we can use [`frame::prelude::ensure`] to express the error slightly better.
|
||||||
|
//! This macro will call `.into()` under the hood.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better)]
|
||||||
|
//!
|
||||||
|
//! Moreover, you will learn in the [Defensive Programming
|
||||||
|
//! section](crate::reference_docs::defensive_programming) that it is always recommended to use
|
||||||
|
//! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not
|
||||||
|
//! only take a step in that direction, but also improve the error handing and make it slightly more
|
||||||
|
//! ergonomic.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better_checked)]
|
||||||
|
//!
|
||||||
|
//! This is more or less all the logic that there is in this basic currency pallet!
|
||||||
|
//!
|
||||||
|
//! ### Your First (Test) Runtime
|
||||||
|
//!
|
||||||
|
//! The typical testing code of a pallet lives in a module that imports some preludes useful for
|
||||||
|
//! testing, similar to:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! pub mod pallet {
|
||||||
|
//! // snip -- actually pallet code.
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[cfg(test)]
|
||||||
|
//! mod tests {
|
||||||
|
//! // bring in the testing prelude of frame
|
||||||
|
//! use frame::testing_prelude::*;
|
||||||
|
//! // bring in all pallet items
|
||||||
|
//! use super::pallet::*;
|
||||||
|
//!
|
||||||
|
//! // snip -- rest of the testing code.
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Next, we create a "test runtime" in order to test our pallet. Recall from
|
||||||
|
//! [`crate::pezkuwi_sdk::frame_runtime`] that a runtime is a collection of pallets, expressed
|
||||||
|
//! through [`frame::runtime::prelude::construct_runtime`]. All runtimes also have to include
|
||||||
|
//! [`frame::prelude::frame_system`]. So we expect to see a runtime with two pallet, `frame_system`
|
||||||
|
//! and the one we just wrote.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime)]
|
||||||
|
//!
|
||||||
|
//! > [`frame::pallet_macros::derive_impl`] is a FRAME feature that enables developers to have
|
||||||
|
//! > defaults for associated types.
|
||||||
|
//!
|
||||||
|
//! Recall that within our pallet, (almost) all blocks of code are generic over `<T: Config>`. And,
|
||||||
|
//! because `trait Config: frame_system::Config`, we can get access to all items in `Config` (or
|
||||||
|
//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how
|
||||||
|
//! Rust traits and generics work. If unfamiliar with this pattern, read
|
||||||
|
//! [`crate::reference_docs::trait_based_programming`] before going further.
|
||||||
|
//!
|
||||||
|
//! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct`
|
||||||
|
//! is to implement the `trait Config` of all pallets. That is, anywhere within your pallet code
|
||||||
|
//! where you see `<T: Config>` (read: *"some type `T` that implements `Config`"*), in the runtime,
|
||||||
|
//! it can be replaced with `<Runtime>`, because `Runtime` implements `Config` of all pallets, as we
|
||||||
|
//! see above.
|
||||||
|
//!
|
||||||
|
//! Another way to think about this is that within a pallet, a lot of types are "unknown" and, we
|
||||||
|
//! only know that they will be provided at some later point. For example, when you write
|
||||||
|
//! `T::AccountId` (which is short for `<T as frame_system::Config>::AccountId`) in your pallet,
|
||||||
|
//! you are in fact saying "*Some type `AccountId` that will be known later*". That "later" is in
|
||||||
|
//! fact when you specify these types when you implement all `Config` traits for `Runtime`.
|
||||||
|
//!
|
||||||
|
//! As you see above, `frame_system::Config` is setting the `AccountId` to `u64`. Of course, a real
|
||||||
|
//! runtime will not use this type, and instead reside to a proper type like a 32-byte standard
|
||||||
|
//! public key. This is a HUGE benefit that FRAME developers can tap into: through the framework
|
||||||
|
//! being so generic, different types can always be customized to simple things when needed.
|
||||||
|
//!
|
||||||
|
//! > Imagine how hard it would have been if all tests had to use a real 32-byte account id, as
|
||||||
|
//! > opposed to just a u64 number 🙈.
|
||||||
|
//!
|
||||||
|
//! ### Your First Test
|
||||||
|
//!
|
||||||
|
//! The above is all you need to execute the dispatchables of your pallet. The last thing you need
|
||||||
|
//! to learn is that all of your pallet testing code should be wrapped in
|
||||||
|
//! [`frame::testing_prelude::TestState`]. This is a type that provides access to an in-memory state
|
||||||
|
//! to be used in our tests.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", first_test)]
|
||||||
|
//!
|
||||||
|
//! In the first test, we simply assert that there is no total issuance, and no balance associated
|
||||||
|
//! with Alice's account. Then, we mint some balance into Alice's, and re-check.
|
||||||
|
//!
|
||||||
|
//! As noted above, the `T::AccountId` is now `u64`. Moreover, `Runtime` is replacing `<T: Config>`.
|
||||||
|
//! This is why for example you see `Balances::<Runtime>::get(..)`. Finally, notice that the
|
||||||
|
//! dispatchables are simply functions that can be called on top of the `Pallet` struct.
|
||||||
|
//!
|
||||||
|
//! Congratulations! You have written your first pallet and tested it! Next, we learn a few optional
|
||||||
|
//! steps to improve our pallet.
|
||||||
|
//!
|
||||||
|
//! ## Improving the Currency Pallet
|
||||||
|
//!
|
||||||
|
//! ### Better Test Setup
|
||||||
|
//!
|
||||||
|
//! Idiomatic FRAME pallets often use Builder pattern to define their initial state.
|
||||||
|
//!
|
||||||
|
//! > The Pezkuwi Blockchain Academy's Rust entrance exam has a
|
||||||
|
//! > [section](https://github.com/pezkuwichain/kurdistan_blockchain-akademy/blob/main/src/m_builder.rs)
|
||||||
|
//! > on this that you can use to learn the Builder Pattern.
|
||||||
|
//!
|
||||||
|
//! Let's see how we can implement a better test setup using this pattern. First, we define a
|
||||||
|
//! `struct StateBuilder`.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", StateBuilder)]
|
||||||
|
//!
|
||||||
|
//! This struct is meant to contain the same list of accounts and balances that we want to have at
|
||||||
|
//! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE, 100), (2, 100)];`
|
||||||
|
//! so far. Then, if desired, we attach a default value for this struct.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", default_state_builder)]
|
||||||
|
//!
|
||||||
|
//! Like any other builder pattern, we attach functions to the type to mutate its internal
|
||||||
|
//! properties.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_add)]
|
||||||
|
//!
|
||||||
|
//! Finally --the useful part-- we write our own custom `build_and_execute` function on
|
||||||
|
//! this type. This function will do multiple things:
|
||||||
|
//!
|
||||||
|
//! 1. It would consume `self` to produce our `TestState` based on the properties that we attached
|
||||||
|
//! to `self`.
|
||||||
|
//! 2. It would execute any test function that we pass in as closure.
|
||||||
|
//! 3. A nifty trick, this allows our test setup to have some code that is executed both before and
|
||||||
|
//! after each test. For example, in this test, we do some additional checking about the
|
||||||
|
//! correctness of the `TotalIssuance`. We leave it up to you as an exercise to learn why the
|
||||||
|
//! assertion should always hold, and how it is checked.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_build)]
|
||||||
|
//!
|
||||||
|
//! We can write tests that specifically check the initial state, and making sure our `StateBuilder`
|
||||||
|
//! is working exactly as intended.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_works)]
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_add_balance)]
|
||||||
|
//!
|
||||||
|
//! ### More Tests
|
||||||
|
//!
|
||||||
|
//! Now that we have a more ergonomic test setup, let's see how a well written test for transfer and
|
||||||
|
//! mint would look like.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_works)]
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", mint_works)]
|
||||||
|
//!
|
||||||
|
//! It is always a good idea to build a mental model where you write *at least* one test for each
|
||||||
|
//! "success path" of a dispatchable, and one test for each "failure path", such as:
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_from_non_existent_fails)]
|
||||||
|
//!
|
||||||
|
//! We leave it up to you to write a test that triggers the `InsufficientBalance` error.
|
||||||
|
//!
|
||||||
|
//! ### Event and Error
|
||||||
|
//!
|
||||||
|
//! Our pallet is mainly missing two parts that are common in most FRAME pallets: Events, and
|
||||||
|
//! Errors. First, let's understand what each is.
|
||||||
|
//!
|
||||||
|
//! - **Error**: The static string-based error scheme we used so far is good for readability, but it
|
||||||
|
//! has a few drawbacks. The biggest problem with strings are that they are not type safe, e.g. a
|
||||||
|
//! match statement cannot be exhaustive. These string literals will bloat the final wasm blob,
|
||||||
|
//! and are relatively heavy to transmit and encode/decode. Moreover, it is easy to mistype them
|
||||||
|
//! by one character. FRAME errors are exactly a solution to maintain readability, whilst fixing
|
||||||
|
//! the drawbacks mentioned. In short, we use an enum to represent different variants of our
|
||||||
|
//! error. These variants are then mapped in an efficient way (using only `u8` indices) to
|
||||||
|
//! [`sp_runtime::DispatchError::Module`]. Read more about this in [`pallet::error`].
|
||||||
|
//!
|
||||||
|
//! - **Event**: Events are akin to the return type of dispatchables. They are mostly data blobs
|
||||||
|
//! emitted by the runtime to let outside world know what is happening inside the pallet. Since
|
||||||
|
//! otherwise, the outside world does not have an easy access to the state changes. They should
|
||||||
|
//! represent what happened at the end of a dispatch operation. Therefore, the convention is to
|
||||||
|
//! use passive tense for event names (eg. `SomethingHappened`). This allows other sub-systems or
|
||||||
|
//! external parties (eg. a light-node, a DApp) to listen to particular events happening, without
|
||||||
|
//! needing to re-execute the whole state transition function.
|
||||||
|
//!
|
||||||
|
//! With the explanation out of the way, let's see how these components can be added. Both follow a
|
||||||
|
//! fairly familiar syntax: normal Rust enums, with extra [`pallet::event`] and [`pallet::error`]
|
||||||
|
//! attributes attached.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Event)]
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Error)]
|
||||||
|
//!
|
||||||
|
//! One slightly custom part of this is the [`pallet::generate_deposit`] part. Without going into
|
||||||
|
//! too much detail, in order for a pallet to emit events to the rest of the system, it needs to do
|
||||||
|
//! two things:
|
||||||
|
//!
|
||||||
|
//! 1. Declare a type in its `Config` that refers to the overarching event type of the runtime. In
|
||||||
|
//! short, by doing this, the pallet is expressing an important bound: `type RuntimeEvent:
|
||||||
|
//! From<Event<Self>>`. Read: a `RuntimeEvent` exists, and it can be created from the local `enum
|
||||||
|
//! Event` of this pallet. This enables the pallet to convert its `Event` into `RuntimeEvent`, and
|
||||||
|
//! store it where needed.
|
||||||
|
//!
|
||||||
|
//! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME
|
||||||
|
//! provides a default way of storing events, and this is what [`pallet::generate_deposit`] is
|
||||||
|
//! doing.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", config_v2)]
|
||||||
|
//!
|
||||||
|
//! > These `Runtime*` types are better explained in
|
||||||
|
//! > [`crate::reference_docs::frame_runtime_types`].
|
||||||
|
//!
|
||||||
|
//! Then, we can rewrite the `transfer` dispatchable as such:
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_v2)]
|
||||||
|
//!
|
||||||
|
//! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime
|
||||||
|
//! setup.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime_v2)]
|
||||||
|
//!
|
||||||
|
//! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent =
|
||||||
|
//! RuntimeEvent`) is generated by
|
||||||
|
//! [`construct_runtime`](frame::runtime::prelude::construct_runtime). An interesting way to inspect
|
||||||
|
//! this type is to see its definition in rust-docs:
|
||||||
|
//! [`crate::guides::your_first_pallet::pallet_v2::tests::runtime_v2::RuntimeEvent`].
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ## What Next?
|
||||||
|
//!
|
||||||
|
//! The following topics where used in this guide, but not covered in depth. It is suggested to
|
||||||
|
//! study them subsequently:
|
||||||
|
//!
|
||||||
|
//! - [`crate::reference_docs::defensive_programming`].
|
||||||
|
//! - [`crate::reference_docs::frame_origin`].
|
||||||
|
//! - [`crate::reference_docs::frame_runtime_types`].
|
||||||
|
//! - The pallet we wrote in this guide was using `dev_mode`, learn more in [`pallet::config`].
|
||||||
|
//! - Learn more about the individual pallet items/macros, such as event and errors and call, in
|
||||||
|
//! [`frame::pallet_macros`].
|
||||||
|
//!
|
||||||
|
//! [`pallet::storage`]: frame_support::pallet_macros::storage
|
||||||
|
//! [`pallet::call`]: frame_support::pallet_macros::call
|
||||||
|
//! [`pallet::event`]: frame_support::pallet_macros::event
|
||||||
|
//! [`pallet::error`]: frame_support::pallet_macros::error
|
||||||
|
//! [`pallet::pallet`]: frame_support::pallet
|
||||||
|
//! [`pallet::config`]: frame_support::pallet_macros::config
|
||||||
|
//! [`pallet::generate_deposit`]: frame_support::pallet_macros::generate_deposit
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[frame::pallet(dev_mode)]
|
||||||
|
pub mod shell_pallet {
|
||||||
|
use frame::prelude::*;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frame::pallet(dev_mode)]
|
||||||
|
pub mod pallet {
|
||||||
|
use frame::prelude::*;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
pub type Balance = u128;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
/// Single storage item, of type `Balance`.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
/// A mapping from `T::AccountId` to `Balance`
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
||||||
|
|
||||||
|
#[docify::export(impl_pallet)]
|
||||||
|
#[pallet::call]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
/// An unsafe mint that can be called by anyone. Not a great idea.
|
||||||
|
pub fn mint_unsafe(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
// ensure that this is a signed account, but we don't really check `_anyone`.
|
||||||
|
let _anyone = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
// update the balances map. Notice how all `<T: Config>` remains as `<T>`.
|
||||||
|
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
// update total issuance.
|
||||||
|
TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transfer `amount` from `origin` to `dest`.
|
||||||
|
pub fn transfer(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let sender = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
||||||
|
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||||
|
if sender_balance < amount {
|
||||||
|
return Err("InsufficientBalance".into());
|
||||||
|
}
|
||||||
|
let remainder = sender_balance - amount;
|
||||||
|
|
||||||
|
// update sender and dest balances.
|
||||||
|
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
Balances::<T>::insert(&sender, remainder);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[docify::export]
|
||||||
|
pub fn transfer_better(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let sender = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||||
|
ensure!(sender_balance >= amount, "InsufficientBalance");
|
||||||
|
let remainder = sender_balance - amount;
|
||||||
|
|
||||||
|
// .. snip
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
/// Transfer `amount` from `origin` to `dest`.
|
||||||
|
pub fn transfer_better_checked(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let sender = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
|
||||||
|
let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
|
||||||
|
|
||||||
|
// .. snip
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, doc))]
|
||||||
|
pub(crate) mod tests {
|
||||||
|
use crate::guides::your_first_pallet::pallet::*;
|
||||||
|
|
||||||
|
#[docify::export(testing_prelude)]
|
||||||
|
use frame::testing_prelude::*;
|
||||||
|
|
||||||
|
pub(crate) const ALICE: u64 = 1;
|
||||||
|
pub(crate) const BOB: u64 = 2;
|
||||||
|
pub(crate) const CHARLIE: u64 = 3;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
// This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
|
||||||
|
// tests { .. }`
|
||||||
|
mod runtime {
|
||||||
|
use super::*;
|
||||||
|
// we need to reference our `mod pallet` as an identifier to pass to
|
||||||
|
// `construct_runtime`.
|
||||||
|
// YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
|
||||||
|
use crate::guides::your_first_pallet::pallet as pallet_currency;
|
||||||
|
|
||||||
|
construct_runtime!(
|
||||||
|
pub enum Runtime {
|
||||||
|
// ---^^^^^^ This is where `enum Runtime` is defined.
|
||||||
|
System: frame_system,
|
||||||
|
Currency: pallet_currency,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||||
|
impl frame_system::Config for Runtime {
|
||||||
|
type Block = MockBlock<Runtime>;
|
||||||
|
// within pallet we just said `<T as frame_system::Config>::AccountId`, now we
|
||||||
|
// finally specified it.
|
||||||
|
type AccountId = u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// our simple pallet has nothing to be configured.
|
||||||
|
impl pallet_currency::Config for Runtime {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use runtime::*;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
#[docify::export]
|
||||||
|
fn new_test_state_basic() -> TestState {
|
||||||
|
let mut state = TestState::new_empty();
|
||||||
|
let accounts = vec![(ALICE, 100), (BOB, 100)];
|
||||||
|
state.execute_with(|| {
|
||||||
|
for (who, amount) in &accounts {
|
||||||
|
Balances::<Runtime>::insert(who, amount);
|
||||||
|
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
pub(crate) struct StateBuilder {
|
||||||
|
balances: Vec<(<Runtime as frame_system::Config>::AccountId, Balance)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export(default_state_builder)]
|
||||||
|
impl Default for StateBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { balances: vec![(ALICE, 100), (BOB, 100)] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export(impl_state_builder_add)]
|
||||||
|
impl StateBuilder {
|
||||||
|
fn add_balance(
|
||||||
|
mut self,
|
||||||
|
who: <Runtime as frame_system::Config>::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> Self {
|
||||||
|
self.balances.push((who, amount));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export(impl_state_builder_build)]
|
||||||
|
impl StateBuilder {
|
||||||
|
pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
|
||||||
|
let mut ext = TestState::new_empty();
|
||||||
|
ext.execute_with(|| {
|
||||||
|
for (who, amount) in &self.balances {
|
||||||
|
Balances::<Runtime>::insert(who, amount);
|
||||||
|
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ext.execute_with(test);
|
||||||
|
|
||||||
|
// assertions that must always hold
|
||||||
|
ext.execute_with(|| {
|
||||||
|
assert_eq!(
|
||||||
|
Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
|
||||||
|
TotalIssuance::<Runtime>::get().unwrap_or_default()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn first_test() {
|
||||||
|
TestState::new_empty().execute_with(|| {
|
||||||
|
// We expect Alice's account to have no funds.
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), None);
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), None);
|
||||||
|
|
||||||
|
// mint some funds into Alice's account.
|
||||||
|
assert_ok!(Pallet::<Runtime>::mint_unsafe(
|
||||||
|
RuntimeOrigin::signed(ALICE),
|
||||||
|
ALICE,
|
||||||
|
100
|
||||||
|
));
|
||||||
|
|
||||||
|
// re-check the above
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn state_builder_works() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn state_builder_add_balance() {
|
||||||
|
StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn state_builder_duplicate_genesis_fails() {
|
||||||
|
StateBuilder::default()
|
||||||
|
.add_balance(CHARLIE, 42)
|
||||||
|
.add_balance(CHARLIE, 43)
|
||||||
|
.build_and_execute(|| {
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn mint_works() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
// given the initial state, when:
|
||||||
|
assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
|
||||||
|
|
||||||
|
// given:
|
||||||
|
assert_ok!(Pallet::<Runtime>::mint_unsafe(
|
||||||
|
RuntimeOrigin::signed(ALICE),
|
||||||
|
CHARLIE,
|
||||||
|
100
|
||||||
|
));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn transfer_works() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
// given the initial state, when:
|
||||||
|
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
|
||||||
|
// when:
|
||||||
|
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[test]
|
||||||
|
fn transfer_from_non_existent_fails() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
// given the initial state, when:
|
||||||
|
assert_err!(
|
||||||
|
Pallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
|
||||||
|
"NonExistentAccount"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then nothing has changed.
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frame::pallet(dev_mode)]
|
||||||
|
pub mod pallet_v2 {
|
||||||
|
use super::pallet::Balance;
|
||||||
|
use frame::prelude::*;
|
||||||
|
|
||||||
|
#[docify::export(config_v2)]
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {
|
||||||
|
/// The overarching event type of the runtime.
|
||||||
|
#[allow(deprecated)]
|
||||||
|
type RuntimeEvent: From<Event<Self>>
|
||||||
|
+ IsType<<Self as frame_system::Config>::RuntimeEvent>
|
||||||
|
+ TryInto<Event<Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
|
||||||
|
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[pallet::error]
|
||||||
|
pub enum Error<T> {
|
||||||
|
/// Account does not exist.
|
||||||
|
NonExistentAccount,
|
||||||
|
/// Account does not have enough balance.
|
||||||
|
InsufficientBalance,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
#[pallet::event]
|
||||||
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||||
|
pub enum Event<T: Config> {
|
||||||
|
/// A transfer succeeded.
|
||||||
|
Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::call]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[docify::export(transfer_v2)]
|
||||||
|
pub fn transfer(
|
||||||
|
origin: T::RuntimeOrigin,
|
||||||
|
dest: T::AccountId,
|
||||||
|
amount: Balance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let sender = ensure_signed(origin)?;
|
||||||
|
|
||||||
|
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
|
||||||
|
let sender_balance =
|
||||||
|
Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
|
||||||
|
let remainder =
|
||||||
|
sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
|
||||||
|
|
||||||
|
Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||||
|
Balances::<T>::insert(&sender, remainder);
|
||||||
|
|
||||||
|
Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, doc))]
|
||||||
|
pub mod tests {
|
||||||
|
use super::{super::pallet::tests::StateBuilder, *};
|
||||||
|
use frame::testing_prelude::*;
|
||||||
|
const ALICE: u64 = 1;
|
||||||
|
const BOB: u64 = 2;
|
||||||
|
|
||||||
|
#[docify::export]
|
||||||
|
pub mod runtime_v2 {
|
||||||
|
use super::*;
|
||||||
|
use crate::guides::your_first_pallet::pallet_v2 as pallet_currency;
|
||||||
|
|
||||||
|
construct_runtime!(
|
||||||
|
pub enum Runtime {
|
||||||
|
System: frame_system,
|
||||||
|
Currency: pallet_currency,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||||
|
impl frame_system::Config for Runtime {
|
||||||
|
type Block = MockBlock<Runtime>;
|
||||||
|
type AccountId = u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl pallet_currency::Config for Runtime {
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use runtime_v2::*;
|
||||||
|
|
||||||
|
#[docify::export(transfer_works_v2)]
|
||||||
|
#[test]
|
||||||
|
fn transfer_works() {
|
||||||
|
StateBuilder::default().build_and_execute(|| {
|
||||||
|
// skip the genesis block, as events are not deposited there and we need them for
|
||||||
|
// the final assertion.
|
||||||
|
System::set_block_number(ALICE);
|
||||||
|
|
||||||
|
// given the initial state, when:
|
||||||
|
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
|
||||||
|
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
|
||||||
|
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||||
|
|
||||||
|
// now we can also check that an event has been deposited:
|
||||||
|
assert_eq!(
|
||||||
|
System::read_events_for_pallet::<Event<Runtime>>(),
|
||||||
|
vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
//! # Your first Runtime
|
||||||
|
//!
|
||||||
|
//! This guide will walk you through the steps to add your pallet to a runtime.
|
||||||
|
//!
|
||||||
|
//! The good news is, in [`crate::guides::your_first_pallet`], we have already created a _test_
|
||||||
|
//! runtime that was used for testing, and a real runtime is not that much different!
|
||||||
|
//!
|
||||||
|
//! ## Setup
|
||||||
|
//!
|
||||||
|
//! A runtime shares a few similar setup requirements as with a pallet:
|
||||||
|
//!
|
||||||
|
//! * importing [`frame`], [`codec`], and [`scale_info`] crates.
|
||||||
|
//! * following the [`std` feature-gating](crate::pezkuwi_sdk::substrate#wasm-build) pattern.
|
||||||
|
//!
|
||||||
|
//! But, more specifically, it also contains:
|
||||||
|
//!
|
||||||
|
//! * a `build.rs` that uses [`substrate_wasm_builder`]. This entails declaring
|
||||||
|
//! `[build-dependencies]` in the Cargo manifest file:
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! [build-dependencies]
|
||||||
|
//! substrate-wasm-builder = { ... }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! >Note that a runtime must always be one-runtime-per-crate. You cannot define multiple runtimes
|
||||||
|
//! per rust crate.
|
||||||
|
//!
|
||||||
|
//! You can find the full code of this guide in [`first_runtime`].
|
||||||
|
//!
|
||||||
|
//! ## Your First Runtime
|
||||||
|
//!
|
||||||
|
//! The first new property of a real runtime that it must define its
|
||||||
|
//! [`frame::runtime::prelude::RuntimeVersion`]:
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", VERSION)]
|
||||||
|
//!
|
||||||
|
//! The version contains a number of very important fields, such as `spec_version` and `spec_name`
|
||||||
|
//! that play an important role in identifying your runtime and its version, more importantly in
|
||||||
|
//! runtime upgrades. More about runtime upgrades in
|
||||||
|
//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`].
|
||||||
|
//!
|
||||||
|
//! Then, a real runtime also contains the `impl` of all individual pallets' `trait Config` for
|
||||||
|
//! `struct Runtime`, and a [`frame::runtime::prelude::construct_runtime`] macro that amalgamates
|
||||||
|
//! them all.
|
||||||
|
//!
|
||||||
|
//! In the case of our example:
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", our_config_impl)]
|
||||||
|
//!
|
||||||
|
//! In this example, we bring in a number of other pallets from [`frame`] into the runtime, each of
|
||||||
|
//! their `Config` need to be implemented for `struct Runtime`:
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", config_impls)]
|
||||||
|
//!
|
||||||
|
//! Notice how we use [`frame::pallet_macros::derive_impl`] to provide "default" configuration items
|
||||||
|
//! for each pallet. Feel free to dive into the definition of each default prelude (eg.
|
||||||
|
//! [`frame::prelude::frame_system::pallet::config_preludes`]) to learn more which types are exactly
|
||||||
|
//! used.
|
||||||
|
//!
|
||||||
|
//! Recall that in test runtime in [`crate::guides::your_first_pallet`], we provided `type AccountId
|
||||||
|
//! = u64` to `frame_system`, while in this case we rely on whatever is provided by
|
||||||
|
//! [`SolochainDefaultConfig`], which is indeed a "real" 32 byte account id.
|
||||||
|
//!
|
||||||
|
//! Then, a familiar instance of `construct_runtime` amalgamates all of the pallets:
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", cr)]
|
||||||
|
//!
|
||||||
|
//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that every (real) runtime needs to
|
||||||
|
//! implement a set of runtime APIs that will then let the node to communicate with it. The final
|
||||||
|
//! steps of crafting a runtime are related to achieving exactly this.
|
||||||
|
//!
|
||||||
|
//! First, we define a number of types that eventually lead to the creation of an instance of
|
||||||
|
//! [`frame::runtime::prelude::Executive`]. The executive is a handy FRAME utility that, through
|
||||||
|
//! amalgamating all pallets and further types, implements some of the very very core pieces of the
|
||||||
|
//! runtime logic, such as how blocks are executed and other runtime-api implementations.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", runtime_types)]
|
||||||
|
//!
|
||||||
|
//! Finally, we use [`frame::runtime::prelude::impl_runtime_apis`] to implement all of the runtime
|
||||||
|
//! APIs that the runtime wishes to expose. As you will see in the code, most of these runtime API
|
||||||
|
//! implementations are merely forwarding calls to `RuntimeExecutive` which handles the actual
|
||||||
|
//! logic. Given that the implementation block is somewhat large, we won't repeat it here. You can
|
||||||
|
//! look for `impl_runtime_apis!` in [`first_runtime`].
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! impl_runtime_apis! {
|
||||||
|
//! impl apis::Core<Block> for Runtime {
|
||||||
|
//! fn version() -> RuntimeVersion {
|
||||||
|
//! VERSION
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn execute_block(block: Block) {
|
||||||
|
//! RuntimeExecutive::execute_block(block)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn initialize_block(header: &Header) -> ExtrinsicInclusionMode {
|
||||||
|
//! RuntimeExecutive::initialize_block(header)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // many more trait impls...
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! And that more or less covers the details of how you would write a real runtime!
|
||||||
|
//!
|
||||||
|
//! Once you compile a crate that contains a runtime as above, simply running `cargo build` will
|
||||||
|
//! generate the wasm blobs and place them under `./target/release/wbuild`, as explained
|
||||||
|
//! [here](crate::pezkuwi_sdk::substrate#wasm-build).
|
||||||
|
//!
|
||||||
|
//! ## Genesis Configuration
|
||||||
|
//!
|
||||||
|
//! Every runtime specifies a number of runtime APIs that help the outer world (most notably, a
|
||||||
|
//! `node`) know what is the genesis state of this runtime. These APIs are then used to generate
|
||||||
|
//! what is known as a **Chain Specification, or chain spec for short**. A chain spec is the
|
||||||
|
//! primary way to run a new chain.
|
||||||
|
//!
|
||||||
|
//! These APIs are defined in [`sp_genesis_builder`], and are re-exposed as a part of
|
||||||
|
//! [`frame::runtime::apis`]. Therefore, the implementation blocks can be found inside of
|
||||||
|
//! `impl_runtime_apis!` similar to:
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! impl_runtime_apis! {
|
||||||
|
//! impl apis::GenesisBuilder<Block> for Runtime {
|
||||||
|
//! fn build_state(config: Vec<u8>) -> GenesisBuilderResult {
|
||||||
|
//! 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> {
|
||||||
|
//! crate::genesis_config_presets::preset_names()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The implementation of these function can naturally vary from one runtime to the other, but the
|
||||||
|
//! overall pattern is common. For the case of this runtime, we do the following:
|
||||||
|
//!
|
||||||
|
//! 1. Expose one non-default preset, namely [`sp_genesis_builder::DEV_RUNTIME_PRESET`]. This means
|
||||||
|
//! our runtime has two "presets" of genesis state in total: `DEV_RUNTIME_PRESET` and `None`.
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", preset_names)]
|
||||||
|
//!
|
||||||
|
//! For `build_state` and `get_preset`, we use the helper functions provide by frame:
|
||||||
|
//!
|
||||||
|
//! * [`frame::runtime::prelude::build_state`] and [`frame::runtime::prelude::get_preset`].
|
||||||
|
//!
|
||||||
|
//! Indeed, our runtime needs to specify what its `DEV_RUNTIME_PRESET` genesis state should be like:
|
||||||
|
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", development_config_genesis)]
|
||||||
|
//!
|
||||||
|
//! For more in-depth information about `GenesisConfig`, `ChainSpec`, the `GenesisBuilder` API and
|
||||||
|
//! `chain-spec-builder`, see [`crate::reference_docs::chain_spec_genesis`].
|
||||||
|
//!
|
||||||
|
//! ## Next Step
|
||||||
|
//!
|
||||||
|
//! See [`crate::guides::your_first_node`].
|
||||||
|
//!
|
||||||
|
//! ## Further Reading
|
||||||
|
//!
|
||||||
|
//! 1. To learn more about signed extensions, see [`crate::reference_docs::signed_extensions`].
|
||||||
|
//! 2. `AllPalletsWithSystem` is also generated by `construct_runtime`, as explained in
|
||||||
|
//! [`crate::reference_docs::frame_runtime_types`].
|
||||||
|
//! 3. `Executive` supports more generics, most notably allowing the runtime to configure more
|
||||||
|
//! runtime migrations, as explained in
|
||||||
|
//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`].
|
||||||
|
//! 4. Learn more about adding and implementing runtime apis in
|
||||||
|
//! [`crate::reference_docs::custom_runtime_api_rpc`].
|
||||||
|
//! 5. To see a complete example of a runtime+pallet that is similar to this guide, please see
|
||||||
|
//! [`crate::pezkuwi_sdk::templates`].
|
||||||
|
//!
|
||||||
|
//! [`SolochainDefaultConfig`]: struct@frame_system::pallet::config_preludes::SolochainDefaultConfig
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use cmd_lib::run_cmd;
|
||||||
|
|
||||||
|
const FIRST_RUNTIME: &'static str = "pezkuwi-sdk-docs-first-runtime";
|
||||||
|
|
||||||
|
#[docify::export_content]
|
||||||
|
#[test]
|
||||||
|
fn build_runtime() {
|
||||||
|
run_cmd!(
|
||||||
|
cargo build --release -p $FIRST_RUNTIME
|
||||||
|
)
|
||||||
|
.expect("Failed to run command");
|
||||||
|
}
|
||||||
|
}
|
||||||