docs: add Pezkuwi-SDK documentation

- Technical documentation
- SDK guides
- Architecture overview
- Whitepaper
- Contributor guides
This commit is contained in:
2025-12-13 05:55:40 +03:00
commit b4dfaaf5bb
154 changed files with 13550 additions and 0 deletions
+22
View File
@@ -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
+30
View File
@@ -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:
![backport](./images/backport-ex2.png)
+191
View File
@@ -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.
+76
View File
@@ -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
+221
View File
@@ -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 youre 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 doesnt 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).
+77
View File
@@ -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.
Dont 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
```
+354
View File
@@ -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).
+74
View File
@@ -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
+124
View File
@@ -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.
- Dont 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-----
```
+164
View File
@@ -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)
+43
View File
@@ -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`
+73
View File
@@ -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
```
+173
View File
@@ -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).
+20
View File
@@ -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...)
+150
View File
@@ -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 youre confident the change is safe and justified.
+71
View File
@@ -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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

+342
View File
@@ -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*
+12
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
flowchart TD
E(Extrinsic) ---> I(Inherent);
E --> T(Transaction)
T --> ST("Signed (aka. Transaction)")
T --> UT(Unsigned)
+3
View File
@@ -0,0 +1,3 @@
flowchart LR
RuntimeCall --"TryInto"--> PalletCall
PalletCall --"Into"--> RuntimeCall
+10
View File
@@ -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
+8
View File
@@ -0,0 +1,8 @@
flowchart LR
subgraph PezkuwiSDKChain[A Pezkuwi SDK-based blockchain]
Node
Runtime
end
FRAME -.-> Runtime
PezkuwiSDK[Pezkuwi SDK Node Libraries] -.-> Node
+11
View File
@@ -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
+16
View File
@@ -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
+21
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
flowchart LR
B[Block] --> STF
S[State] --> STF
STF --> NS[New State]
+12
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
flowchart LR
T[Using a Template] --> P[Writing Your Own FRAME-Based Pallet] --> C[Custom Node]
+8
View File
@@ -0,0 +1,8 @@
graph TB
subgraph Substrate
direction LR
subgraph Node
end
subgraph Runtime
end
end
+20
View File
@@ -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
+687
View File
@@ -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
+213
View File
@@ -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",
]
+2
View File
@@ -0,0 +1,2 @@
<script> mermaid.init({ startOnLoad: true, theme: "dark" }, "pre.language-mermaid > code");</script>
+147
View File
@@ -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>
+57
View File
@@ -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"]
+481
View File
@@ -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;
}
+14
View File
@@ -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/)
+254
View File
@@ -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 teyrchains 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 teyrchains 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 isnt 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)]
+1
View File
@@ -0,0 +1 @@
//! # Changing Consensus
@@ -0,0 +1 @@
//! # Cumulus Enabled Teyrchain
+35
View File
@@ -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.
+182
View File
@@ -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.
+88
View File
@@ -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.
+88
View File
@@ -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`.
+50
View File
@@ -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;
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
//! # XCM Enabled Teyrchain
+342
View File
@@ -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;
}
}
+789
View File
@@ -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 }]
);
});
}
}
}
+186
View File
@@ -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");
}
}

Some files were not shown because too many files have changed in this diff Show More