feat(web): add network subpages and subdomains listing page

- Add /subdomains page listing all 20 PezkuwiChain subdomains
- Add Back to Home button to Subdomains page
- Create NetworkPage reusable component for network details
- Add 7 network subpages: /mainnet, /staging, /testnet, /beta, /alfa, /development, /local
- Update ChainSpecs network cards to navigate to network subpages
- Add i18n translations for chainSpecs section in en.ts
- Add SDK docs with rebranding support (rebrand-rustdoc.cjs)
- Add generate-docs-structure.cjs for automatic docs generation
- Update shared libs: endpoints, polkadot, wallet, xcm-bridge
- Add new token logos: TYR, ZGR, pezkuwi_icon
- Add new pages: Explorer, Docs, Wallet, Api, Faucet, Developers, Grants, Wiki, Forum, Telemetry
This commit is contained in:
2025-12-11 00:33:47 +03:00
parent 2c6c4f5606
commit 11678fe7cd
976 changed files with 60601 additions and 168 deletions
@@ -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://dotcodeschool.com/)
//! - [Pezkuwi Developers Github Organization](https://github.com/polkadot-developers/)
//! - [Pezkuwi Blockchain Academy](https://github.com/pezkuwichain/kurdistan_blockchain-akademy)
//! - [Pezkuwi Wiki](https://wiki.network.pezkuwichain.io/)
@@ -0,0 +1,254 @@
//! # Upgrade Teyrchain for Asynchronous Backing Compatibility
//!
//! This guide is relevant for cumulus based teyrchain projects started in 2023 or before, whose
//! backing process is synchronous where parablocks can only be built on the latest Relay Chain
//! block. Async Backing allows collators to build parablocks on older Relay Chain blocks and create
//! pipelines of multiple pending parablocks. This parallel block generation increases efficiency
//! and throughput. For more information on Async backing and its terminology, refer to the document
//! on [the Pezkuwi SDK docs.](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/async_backing_guide/index.html)
//!
//! > If starting a new teyrchain project, please use an async backing compatible template such as
//! > the
//! > [teyrchain template](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/templates/teyrchain).
//! The rollout process for Async Backing has three phases. Phases 1 and 2 below put new
//! infrastructure in place. Then we can simply turn on async backing in phase 3.
//!
//! ## Prerequisite
//!
//! The relay chain needs to have async backing enabled so double-check that the relay-chain
//! configuration contains the following three parameters (especially when testing locally e.g. with
//! zombienet):
//!
//! ```json
//! "async_backing_params": {
//! "max_candidate_depth": 3,
//! "allowed_ancestry_len": 2
//! },
//! "scheduling_lookahead": 2
//! ```
//!
//! <div class="warning"><code>scheduling_lookahead</code> must be set to 2, otherwise teyrchain
//! block times will degrade to worse than with sync backing!</div>
//!
//! ## Phase 1 - Update Teyrchain Runtime
//!
//! This phase involves configuring your 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)]
@@ -0,0 +1 @@
//! # Changing Consensus
@@ -0,0 +1 @@
//! # Cumulus Enabled Teyrchain
@@ -0,0 +1,182 @@
//! # Enable elastic scaling for a teyrchain
//!
//! <div class="warning">This guide assumes full familiarity with Asynchronous Backing and its
//! terminology, as defined in <a href="https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/guides/async_backing_guide/index.html">the Pezkuwi SDK Docs</a>.
//! </div>
//!
//! ## Quick introduction to Elastic Scaling
//!
//! [Elastic scaling](https://www.parity.io/blog/polkadot-web3-cloud) is a feature that enables teyrchains (rollups) to use multiple cores.
//! Teyrchains can adjust their usage of core resources on the fly to increase TPS and decrease
//! latency.
//!
//! ### When do you need Elastic Scaling?
//!
//! Depending on their use case, applications might have an increased need for the following:
//! - compute (CPU weight)
//! - bandwidth (proof size)
//! - lower latency (block time)
//!
//! ### High throughput (TPS) and lower latency
//!
//! If the main bottleneck is the CPU, then your teyrchain needs to maximize the compute usage of
//! each core while also achieving a lower latency.
//! 3 cores provide the best balance between CPU, bandwidth and latency: up to 6s of execution,
//! 5MB/s of DA bandwidth and fast block time of just 2 seconds.
//!
//! ### High bandwidth
//!
//! Useful for applications that are bottlenecked by bandwidth.
//! By using 6 cores, applications can make use of up to 6s of compute, 10MB/s of bandwidth
//! while also achieving 1 second block times.
//!
//! ### Ultra low latency
//!
//! When latency is the primary requirement, Elastic scaling is currently the only solution. The
//! caveat is the efficiency of core time usage decreases as more cores are used.
//!
//! For example, using 12 cores enables fast transaction confirmations with 500ms blocks and up to
//! 20 MB/s of DA bandwidth.
//!
//! ## Dependencies
//!
//! Prerequisites: Pezkuwi-SDK `2509` or newer.
//!
//! To ensure the security and reliability of your chain when using this feature you need the
//! following:
//! - An omni-node based collator. This has already become the default choice for collators.
//! - UMP signal support.
//! [RFC103](https://github.com/polkadot-fellows/RFCs/blob/main/text/0103-introduce-core-index-commitment.md).
//! This is mandatory protection against PoV replay attacks.
//! - Enabling the relay parent offset feature. This is required to ensure the teyrchain block times
//! and transaction in-block confidence are not negatively affected by relay chain forks. Read
//! [`crate::guides::handling_teyrchain_forks`] for more information.
//! - Block production configuration adjustments.
//!
//! ### Upgrade to Pezkuwi Omni node
//!
//! Your collators need to run `pezkuwi-teyrchain` or `pezkuwi-omni-node` with the `--authoring
//! slot-based` CLI argument.
//! To avoid potential issues and get best performance it is recommeneded to always run the
//! latest release on all of the collators.
//!
//! Further information about omni-node and how to upgrade is available:
//! - [high level docs](https://docs.pezkuwichain.io/develop/toolkit/parachains/polkadot-omni-node/)
//! - [`crate::reference_docs::omni_node`]
//!
//! ### UMP signals
//!
//! UMP signals are now enabled by default in the `teyrchain-system` pallet and are used for
//! elastic scaling. You can find more technical details about UMP signals and their usage for
//! elastic scaling
//! [here](https://github.com/polkadot-fellows/RFCs/blob/main/text/0103-introduce-core-index-commitment.md).
//!
//! ### Enable the relay parent offset feature
//!
//! It is recommended to use an offset of `1`, which is sufficient to eliminate any issues
//! with relay chain forks.
//!
//! Configure the relay parent offset like this:
//! ```ignore
//! /// Build with an offset of 1 behind the relay chain best block.
//! const RELAY_PARENT_OFFSET: u32 = 1;
//!
//! impl cumulus_pallet_teyrchain_system::Config for Runtime {
//! // ...
//! type RelayParentOffset = ConstU32<RELAY_PARENT_OFFSET>;
//! }
//! ```
//!
//! Implement the runtime API to retrieve the offset on the client side.
//! ```ignore
//! impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
//! fn relay_parent_offset() -> u32 {
//! RELAY_PARENT_OFFSET
//! }
//! }
//! ```
//!
//! ### Block production configuration
//!
//! This configuration directly controls the minimum block time and maximum number of cores
//! the teyrchain can use.
//!
//! Example configuration for a 3 core teyrchain:
//! ```ignore
//! /// The upper limit of how many teyrchain blocks are processed by the relay chain per
//! /// parent. Limits the number of blocks authored per slot. This determines the minimum
//! /// block time of the teyrchain:
//! /// `RELAY_CHAIN_SLOT_DURATION_MILLIS/BLOCK_PROCESSING_VELOCITY`
//! const BLOCK_PROCESSING_VELOCITY: u32 = 3;
//!
//! /// Maximum number of blocks simultaneously accepted by the Runtime, not yet included
//! /// into the relay chain.
//! const UNINCLUDED_SEGMENT_CAPACITY: u32 = (2 + RELAY_PARENT_OFFSET) *
//! BLOCK_PROCESSING_VELOCITY + 1;
//!
//! /// Relay chain slot duration, in milliseconds.
//! const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
//!
//! type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook<
//! Runtime,
//! RELAY_CHAIN_SLOT_DURATION_MILLIS,
//! BLOCK_PROCESSING_VELOCITY,
//! UNINCLUDED_SEGMENT_CAPACITY,
//! >;
//!
//! ```
//!
//! ### Teyrchain Slot Duration
//!
//! A common source of confusion is the correct configuration of the `SlotDuration` that is passed
//! to `pallet-aura`.
//! ```ignore
//! impl pallet_aura::Config for Runtime {
//! // ...
//! type SlotDuration = ConstU64<SLOT_DURATION>;
//! }
//! ```
//!
//! The slot duration determines the length of each author's turn and is decoupled from the block
//! production interval. During their slot, authors are allowed to produce multiple blocks. **The
//! slot duration is required to be at least 6s (same as on the relay chain).**
//!
//! **Configuration recommendations:**
//! - For new teyrchains starting from genesis: use a slot duration of 24 seconds
//! - For existing live teyrchains: leave the slot duration unchanged
//!
//!
//! ## Current limitations
//!
//! ### Maximum execution time per relay chain block.
//!
//! Since teyrchain block authoring is sequential, the next block can only be built after
//! the previous one has been imported.
//! At present, a core allows up to 2 seconds of execution per relay chain block.
//!
//! If we assume a 6s teyrchain slot, and each block takes the full 2 seconds to execute,
//! the teyrchain will not be able to fully utilize the compute resources of all 3 cores.
//!
//! If the collator hardware is faster, it can author and import full blocks more quickly,
//! making it possible to utilize even more than 3 cores efficiently.
//!
//! #### Why?
//!
//! Within a 6-second teyrchain slot, collators can author multiple teyrchain blocks.
//! Before building the first block in a slot, the new block author must import the last
//! block produced by the previous author.
//! If the import of the last block is not completed before the next relay chain slot starts,
//! the new author will build on its parent (assuming it was imported). This will create a fork
//! which degrades the teyrchain block confidence and block times.
//!
//! This means that, on reference hardware, a teyrchain with a slot time of 6s can
//! effectively utilize up to 4 seconds of execution per relay chain block, because it needs to
//! ensure the next block author has enough time to import the last block.
//! Hardware with higher single-core performance can enable a teyrchain to fully utilize more
//! cores.
//!
//! ### Fixed factor scaling.
//!
//! For true elasticity, a teyrchain needs to acquire more cores when needed in an automated
//! manner. This functionality is not yet available in the SDK, thus acquiring additional
//! on-demand or bulk cores has to be managed externally.
@@ -0,0 +1,88 @@
//! # Enable metadata hash verification
//!
//! This guide will teach you how to enable the metadata hash verification in your runtime.
//!
//! ## What is metadata hash verification?
//!
//! Each FRAME based runtime exposes metadata about itself. This metadata is used by consumers of
//! the runtime to interpret the state, to construct transactions etc. Part of this metadata are the
//! type information. These type information can be used to e.g. decode storage entries or to decode
//! a transaction. So, the metadata is quite useful for wallets to interact with a FRAME based
//! chain. Online wallets can fetch the metadata directly from any node of the chain they are
//! connected to, but offline wallets can not do this. So, for the offline wallet to have access to
//! the metadata it needs to be transferred and stored on the device. The problem is that the
//! metadata has a size of several hundreds of kilobytes, which takes quite a while to transfer to
//! these offline wallets and the internal storage of these devices is also not big enough to store
//! the metadata for one or more networks. The next problem is that the offline wallet/user can not
//! trust the metadata to be correct. It is very important for the metadata to be correct or
//! otherwise an attacker could change them in a way that the offline wallet decodes a transaction
//! in a different way than what it will be decoded to on chain. So, the user may sign an incorrect
//! transaction leading to unexpected behavior.
//!
//! The metadata hash verification circumvents the issues of the huge metadata and the need to trust
//! some metadata blob to be correct. To generate a hash for the metadata, the metadata is chunked,
//! these chunks are put into a merkle tree and then the root of this merkle tree is the "metadata
//! hash". For a more technical explanation on how it works, see
//! [RFC78](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html). At compile
//! time the metadata hash is generated and "baked" into the runtime. This makes it extremely cheap
//! for the runtime to verify on chain that the metadata hash is correct. By having the runtime
//! verify the hash on chain, the user also doesn't need to trust the offchain metadata. If the
//! metadata hash doesn't match the on chain metadata hash the transaction will be rejected. The
//! metadata hash itself is added to the data of the transaction that is signed, this means the
//! actual hash does not appear in the transaction. On chain the same procedure is repeated with the
//! metadata hash that is known by the runtime and if the metadata hash doesn't match the signature
//! verification will fail. As the metadata hash is actually the root of a merkle tree, the offline
//! wallet can get proofs of individual types to decode a transaction. This means that the offline
//! wallet does not require the entire metadata to be present on the device.
//!
//! ## Integrating metadata hash verification into your runtime
//!
//! The integration of the metadata hash verification is split into two parts, first the actual
//! integration into the runtime and secondly the enabling of the metadata hash generation at
//! compile time.
//!
//! ### Runtime integration
//!
//! From the runtime side only the
//! [`CheckMetadataHash`](frame_metadata_hash_extension::CheckMetadataHash) needs to be added to the
//! list of signed extension:
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", template_signed_extra)]
//!
//! > **Note:**
//! >
//! > Adding the signed extension changes the encoding of the transaction and adds one extra byte
//! > per transaction!
//!
//! This signed extension will make sure to decode the requested `mode` and will add the metadata
//! hash to the signed data depending on the requested `mode`. The `mode` gives the user/wallet
//! control over deciding if the metadata hash should be verified or not. The metadata hash itself
//! is drawn from the `RUNTIME_METADATA_HASH` environment variable. If the environment variable is
//! not set, any transaction that requires the metadata hash is rejected with the error
//! `CannotLookup`. This is a security measurement to prevent including invalid transactions.
//!
//! <div class="warning">
//!
//! The extension does not work with the native runtime, because the
//! `RUNTIME_METADATA_HASH` environment variable is not set when building the
//! `frame-metadata-hash-extension` crate.
//!
//! </div>
//!
//! ### Enable metadata hash generation
//!
//! The metadata hash generation needs to be enabled when building the wasm binary. The
//! `substrate-wasm-builder` supports this out of the box:
#![doc = docify::embed!("../../templates/teyrchain/runtime/build.rs", template_enable_metadata_hash)]
//!
//! > **Note:**
//! >
//! > The `metadata-hash` feature needs to be enabled for the `substrate-wasm-builder` to enable the
//! > code for being able to generate the metadata hash. It is also recommended to put the metadata
//! > hash generation behind a feature in the runtime as shown above. The reason behind is that it
//! > adds a lot of code which increases the compile time and the generation itself also increases
//! > the compile time. Thus, it is recommended to enable the feature only when the metadata hash is
//! > required (e.g. for an on-chain build).
//!
//! The two parameters to `enable_metadata_hash` are the token symbol and the number of decimals of
//! the primary token of the chain. These information are included for the wallets to show token
//! related operations in a more user friendly way.
@@ -0,0 +1,88 @@
//! # Enable storage weight reclaiming
//!
//! This guide will teach you how to enable storage weight reclaiming for a teyrchain. The
//! explanations in this guide assume a project structure similar to the one detailed in
//! the [substrate documentation](crate::pezkuwi_sdk::substrate#anatomy-of-a-binary-crate). Full
//! technical details are available in the original [pull request](https://github.com/paritytech/polkadot-sdk/pull/3002).
//!
//! # What is PoV reclaim?
//! When a teyrchain submits a block to a relay chain like Pezkuwi or Kusama, it sends the block
//! itself and a storage proof. Together they form the Proof-of-Validity (PoV). The PoV allows the
//! relay chain to validate the teyrchain block by re-executing it. Relay chain
//! validators distribute this PoV among themselves over the network. This distribution is costly
//! and limits the size of the storage proof. The storage weight dimension of FRAME weights reflects
//! this cost and limits the size of the storage proof. However, the storage weight determined
//! during [benchmarking](crate::reference_docs::frame_benchmarking_weight) represents the worst
//! case. In reality, runtime operations often consume less space in the storage proof. PoV reclaim
//! offers a mechanism to reclaim the difference between the benchmarked worst-case and the real
//! proof-size consumption.
//!
//!
//! # How to enable PoV reclaim
//! ## 1. Add the host function to your node
//!
//! To reclaim excess storage weight, a teyrchain runtime needs the
//! ability to fetch the size of the storage proof from the node. The reclaim
//! mechanism uses the
//! [`storage_proof_size`](cumulus_primitives_proof_size_hostfunction::storage_proof_size)
//! host function for this purpose. For convenience, cumulus provides
//! [`TeyrchainHostFunctions`](cumulus_client_service::TeyrchainHostFunctions), a set of
//! host functions typically used by cumulus-based teyrchains. In the binary crate of your
//! teyrchain, find the instantiation of the [`WasmExecutor`](sc_executor::WasmExecutor) and set the
//! correct generic type.
//!
//! This example from the teyrchain-template shows a type definition that includes the correct
//! host functions.
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", wasm_executor)]
//!
//! > **Note:**
//! >
//! > If you see error `runtime requires function imports which are not present on the host:
//! > 'env:ext_storage_proof_size_storage_proof_size_version_1'`, it is likely
//! > that this step in the guide was not set up correctly.
//!
//! ## 2. Enable storage proof recording during import
//!
//! The reclaim mechanism reads the size of the currently recorded storage proof multiple times
//! during block authoring and block import. Proof recording during authoring is already enabled on
//! teyrchains. You must also ensure that storage proof recording is enabled during block import.
//! Find where your node builds the fundamental substrate components by calling
//! [`new_full_parts`](sc_service::new_full_parts). Replace this
//! with [`new_full_parts_record_import`](sc_service::new_full_parts_record_import) and
//! pass `true` as the last parameter to enable import recording.
#![doc = docify::embed!("../../templates/teyrchain/node/src/service.rs", component_instantiation)]
//!
//! > **Note:**
//! >
//! > If you see error `Storage root must match that calculated.` during block import, it is likely
//! > that this step in the guide was not
//! > set up correctly.
//!
//! ## 3. Add the TransactionExtension to your runtime
//!
//! In your runtime, you will find a list of TransactionExtensions.
//! To enable the reclaiming,
//! set [`StorageWeightReclaim`](cumulus_pallet_weight_reclaim::StorageWeightReclaim)
//! as a warpper of that list.
//! It is necessary that this extension wraps all the other transaction extensions in order to catch
//! the whole PoV size of the transactions.
//! The extension will check the size of the storage proof before and after an extrinsic execution.
//! It reclaims the difference between the calculated size and the benchmarked size.
#![doc = docify::embed!("../../templates/teyrchain/runtime/src/lib.rs", template_signed_extra)]
//!
//! ## Optional: Verify that reclaim works
//!
//! Start your node with the log target `runtime::storage_reclaim` set to `trace` to enable full
//! logging for `StorageWeightReclaim`. The following log is an example from a local testnet. To
//! trigger the log, execute any extrinsic on the network.
//!
//! ```ignore
//! ...
//! 2024-04-22 17:31:48.014 TRACE runtime::storage_reclaim: [ferdie] Reclaiming storage weight. benchmarked: 3593, consumed: 265 unspent: 0
//! ...
//! ```
//!
//! In the above example we see a benchmarked size of 3593 bytes, while the extrinsic only consumed
//! 265 bytes of proof size. This results in 3328 bytes of reclaim.
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
@@ -0,0 +1,90 @@
//! # Teyrchain forks
//!
//! In this guide, we will examine how AURA-based teyrchains handle forks. AURA (Authority Round) is
//! a consensus mechanism where block authors rotate at fixed time intervals. Each author gets a
//! predetermined time slice during which they are allowed to author a block. On its own, this
//! mechanism is fork-free.
//!
//! However, since the relay chain provides security and serves as the source of truth for
//! teyrchains, the teyrchain is dependent on it. This relationship can introduce complexities that
//! lead to forking scenarios.
//!
//! ## Background
//! Each teyrchain block has a relay parent, which is a relay chain block that provides context to
//! our teyrchain block. The constraints the relay chain imposes on our teyrchain can cause forks
//! under certain conditions. With asynchronous-backing enabled chains, the node side is building
//! blocks on all relay chain forks. This means that no matter which fork of the relay chain
//! ultimately progressed, the teyrchain would have a block ready for that fork. The situation
//! changes when teyrchains want to produce blocks at a faster cadence. In a scenario where a
//! teyrchain might author on 3 cores with elastic scaling, it is not possible to author on all
//! relay chain forks. The time constraints do not allow it. Building on two forks would result in 6
//! blocks. The authoring of these blocks would consume more time than we have available before the
//! next relay chain block arrives. This limitation requires a more fork-resistant approach to
//! block-building.
//!
//! ## Impact of Forks
//! When a relay chain fork occurs and the teyrchain builds on a fork that will not be extended in
//! the future, the blocks built on that fork are lost and need to be rebuilt. This increases
//! latency and reduces throughput, affecting the overall performance of the teyrchain.
//!
//! # Building on Older Pelay Parents
//! Cumulus offers a way to mitigate the occurence of forks. Instead of picking a block at the tip
//! of the relay chain to build blocks, the node side can pick a relay chain block that is older. By
//! building on 12s old relay chain blocks, forks will already have settled and the teyrchain can
//! build fork-free.
//!
//! ```text
//! Without offset:
//! Relay Chain: A --- B --- C --- D --- E
//! \
//! --- D' --- E'
//! Teyrchain: X --- Y --- ? (builds on both D and D', wasting resources)
//!
//! With offset (2 blocks):
//! Relay Chain: A --- B --- C --- D --- E
//! \
//! --- D' --- E'
//! Teyrchain: X(A) - Y (B) - Z (on C, fork already resolved)
//! ```
//! **Note:** It is possible that relay chain forks extend over more than 1-2 blocks. However, it is
//! unlikely.
//! ## Tradeoffs
//! Fork-free teyrchains come with a few tradeoffs:
//! - The latency of incoming XCM messages will be delayed by `N * 6s`, where `N` is the number of
//! relay chain blocks we want to offset by. For example, by building 2 relay chain blocks behind
//! the tip, the XCM latency will be increased by 12 seconds.
//! - The available PoV space will be slightly reduced. Assuming a 10mb PoV, teyrchains need to be
//! ready to sacrifice around 0.5% of PoV space.
//!
//! ## Enabling Guide
//! The decision whether the teyrchain should build on older relay parents is embedded into the
//! runtime. After the changes are implemented, the runtime will enforce that no author can build
//! with an offset smaller than the desired offset. If you wish to keep your current teyrchain
//! behaviour and do not want aforementioned tradeoffs, set the offset to 0.
//!
//! **Note:** The APIs mentioned here are available in `pezkuwi-sdk` versions after `stable-2506`.
//!
//! 1. Define the relay parent offset your teyrchain should respect in the runtime.
//! ```ignore
//! const RELAY_PARENT_OFFSET = 2;
//! ```
//! 2. Pass this constant to the `teyrchain-system` pallet.
//!
//! ```ignore
//! impl cumulus_pallet_teyrchain_system::Config for Runtime {
//! // Other config items here
//! ...
//! type RelayParentOffset = ConstU32<RELAY_PARENT_OFFSET>;
//! }
//! ```
//! 3. Implement the `RelayParentOffsetApi` runtime API for your runtime.
//!
//! ```ignore
//! impl cumulus_primitives_core::RelayParentOffsetApi<Block> for Runtime {
//! fn relay_parent_offset() -> u32 {
//! RELAY_PARENT_OFFSET
//! }
//! }
//! ```
//! 4. Increase the `UNINCLUDED_SEGMENT_CAPICITY` for your runtime. It needs to be increased by
//! `RELAY_PARENT_OFFSET * BLOCK_PROCESSING_VELOCITY`.
+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
@@ -0,0 +1 @@
//! # XCM Enabled Teyrchain
@@ -0,0 +1,342 @@
//! # Your first Node
//!
//! In this guide, you will learn how to run a runtime, such as the one created in
//! [`your_first_runtime`], in a node. Within the context of this guide, we will focus on running
//! the runtime with an [`omni-node`]. Please first read this page to learn about the OmniNode, and
//! other options when it comes to running a node.
//!
//! [`your_first_runtime`] is a runtime with no consensus related code, and therefore can only be
//! executed with a node that also expects no consensus ([`sc_consensus_manual_seal`]).
//! `pezkuwi-omni-node`'s [`--dev-block-time`] precisely does this.
//!
//! > All of the following steps are coded as unit tests of this module. Please see `Source` of the
//! > page for more information.
//!
//! ## Running The Omni Node
//!
//! ### Installs
//!
//! The `pezkuwi-omni-node` can either be downloaded from the latest [Release](https://github.com/pezkuwichain/pezkuwi-sdk/releases/) of `pezkuwi-sdk`,
//! or installed using `cargo`:
//!
//! ```text
//! cargo install pezkuwi-omni-node
//! ```
//!
//! Next, we need to install the `chain-spec-builder`. This is the tool that allows us to build
//! chain-specifications, through interacting with the genesis related APIs of the runtime, as
//! described in [`crate::guides::your_first_runtime#genesis-configuration`].
//!
//! ```text
//! cargo install staging-chain-spec-builder
//! ```
//!
//! > The name of the crate is prefixed with `staging` as the crate name `chain-spec-builder` on
//! > crates.io is already taken and is not controlled by `pezkuwi-sdk` developers.
//!
//! ### Building Runtime
//!
//! Next, we need to build the corresponding runtime that we wish to interact with.
//!
//! ```text
//! cargo build --release -p path-to-runtime
//! ```
//! Equivalent code in tests:
#![doc = docify::embed!("./src/guides/your_first_runtime.rs", build_runtime)]
//!
//! This creates the wasm file under `./target/{release}/wbuild/release` directory.
//!
//! ### Building Chain Spec
//!
//! Next, we can generate the corresponding chain-spec file. For this example, we will use the
//! `development` (`sp_genesis_config::DEVELOPMENT`) preset.
//!
//! Note that we intend to run this chain-spec with `pezkuwi-omni-node`, which is tailored for
//! running teyrchains. This requires the chain-spec to always contain the `para_id` and a
//! `relay_chain` fields, which are provided below as CLI arguments.
//!
//! ```text
//! chain-spec-builder \
//! -c <path-to-output> \
//! create \
//! --relay-chain dontcare \
//! --runtime pezkuwi_sdk_docs_first_runtime.wasm \
//! named-preset development
//! ```
//!
//! Equivalent code in tests:
#![doc = docify::embed!("./src/guides/your_first_node.rs", csb)]
//!
//!
//! ### Running `pezkuwi-omni-node`
//!
//! Finally, we can run the node with the generated chain-spec file. We can also specify the block
//! time using the `--dev-block-time` flag.
//!
//! ```text
//! pezkuwi-omni-node \
//! --tmp \
//! --dev-block-time 1000 \
//! --chain <chain_spec_file>.json
//! ```
//!
//! > Note that we always prefer to use `--tmp` for testing, as it will save the chain state to a
//! > temporary folder, allowing the chain-to be easily restarted without `purge-chain`. See
//! > [`sc_cli::commands::PurgeChainCmd`] and [`sc_cli::commands::RunCmd::tmp`] for more info.
//!
//! This will start the node and import the blocks. Note while using `--dev-block-time`, the node
//! will use the testing-specific manual-seal consensus. This is an efficient way to test the
//! application logic of your runtime, without needing to yet care about consensus, block
//! production, relay-chain and so on.
//!
//! ### Next Steps
//!
//! * See the rest of the steps in [`crate::reference_docs::omni_node#user-journey`].
//!
//! [`runtime`]: crate::reference_docs::glossary#runtime
//! [`node`]: crate::reference_docs::glossary#node
//! [`build_config`]: first_runtime::Runtime#method.build_config
//! [`omni-node`]: crate::reference_docs::omni_node
//! [`--dev-block-time`]: (pezkuwi_omni_node_lib::cli::Cli::dev_block_time)
#[cfg(test)]
mod tests {
use assert_cmd::assert::OutputAssertExt;
use cmd_lib::*;
use rand::Rng;
use sc_chain_spec::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET};
use sp_genesis_builder::PresetId;
use std::{
io::{BufRead, BufReader},
path::PathBuf,
process::{ChildStderr, Command, Stdio},
time::Duration,
};
const PARA_RUNTIME: &'static str = "teyrchain-template-runtime";
const CHAIN_SPEC_BUILDER: &'static str = "chain-spec-builder";
const OMNI_NODE: &'static str = "pezkuwi-omni-node";
fn cargo() -> Command {
Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()))
}
fn get_target_directory() -> Option<PathBuf> {
let output = cargo().arg("metadata").arg("--format-version=1").output().ok()?;
if !output.status.success() {
return None;
}
let metadata: serde_json::Value = serde_json::from_slice(&output.stdout).ok()?;
let target_directory = metadata["target_directory"].as_str()?;
Some(PathBuf::from(target_directory))
}
fn find_release_binary(name: &str) -> Option<PathBuf> {
let target_dir = get_target_directory()?;
let release_path = target_dir.join("release").join(name);
if release_path.exists() {
Some(release_path)
} else {
None
}
}
fn find_wasm(runtime_name: &str) -> Option<PathBuf> {
let target_dir = get_target_directory()?;
let wasm_path = target_dir
.join("release")
.join("wbuild")
.join(runtime_name)
.join(format!("{}.wasm", runtime_name.replace('-', "_")));
if wasm_path.exists() {
Some(wasm_path)
} else {
None
}
}
fn maybe_build_runtimes() {
if find_wasm(&PARA_RUNTIME).is_none() {
println!("Building teyrchain-template-runtime...");
Command::new("cargo")
.arg("build")
.arg("--release")
.arg("-p")
.arg(PARA_RUNTIME)
.assert()
.success();
}
assert!(find_wasm(PARA_RUNTIME).is_some());
}
fn maybe_build_chain_spec_builder() {
if find_release_binary(CHAIN_SPEC_BUILDER).is_none() {
println!("Building chain-spec-builder...");
Command::new("cargo")
.arg("build")
.arg("--release")
.arg("-p")
.arg("staging-chain-spec-builder")
.assert()
.success();
}
assert!(find_release_binary(CHAIN_SPEC_BUILDER).is_some());
}
fn maybe_build_omni_node() {
if find_release_binary(OMNI_NODE).is_none() {
println!("Building pezkuwi-omni-node...");
Command::new("cargo")
.arg("build")
.arg("--release")
.arg("-p")
.arg("pezkuwi-omni-node")
.assert()
.success();
}
}
async fn imported_block_found(stderr: ChildStderr, block: u64, timeout: u64) -> bool {
tokio::time::timeout(Duration::from_secs(timeout), async {
let want = format!("Imported #{}", block);
let reader = BufReader::new(stderr);
let mut found_block = false;
for line in reader.lines() {
if line.unwrap().contains(&want) {
found_block = true;
break;
}
}
found_block
})
.await
.unwrap()
}
async fn test_runtime_preset(
runtime: &'static str,
block_time: u64,
maybe_preset: Option<PresetId>,
) {
sp_tracing::try_init_simple();
maybe_build_runtimes();
maybe_build_chain_spec_builder();
maybe_build_omni_node();
let chain_spec_builder =
find_release_binary(&CHAIN_SPEC_BUILDER).expect("we built it above; qed");
let omni_node = find_release_binary(OMNI_NODE).expect("we built it above; qed");
let runtime_path = find_wasm(runtime).expect("we built it above; qed");
let random_seed: u32 = rand::thread_rng().gen();
let chain_spec_file = std::env::current_dir()
.unwrap()
.join(format!("{}_{}_{}.json", runtime, block_time, random_seed));
Command::new(chain_spec_builder)
.args(["-c", chain_spec_file.to_str().unwrap()])
.arg("create")
.args(["--relay-chain", "dontcare"])
.args(["-r", runtime_path.to_str().unwrap()])
.args(match maybe_preset {
Some(preset) => vec!["named-preset".to_string(), preset.to_string()],
None => vec!["default".to_string()],
})
.assert()
.success();
let mut child = Command::new(omni_node)
.arg("--tmp")
.args(["--chain", chain_spec_file.to_str().unwrap()])
.args(["--dev-block-time", block_time.to_string().as_str()])
.stderr(Stdio::piped())
.spawn()
.unwrap();
// Take stderr and parse it with timeout.
let stderr = child.stderr.take().unwrap();
let expected_blocks = (10_000 / block_time).saturating_div(2);
assert!(expected_blocks > 0, "test configuration is bad, should give it more time");
assert_eq!(imported_block_found(stderr, expected_blocks, 100).await, true);
std::fs::remove_file(chain_spec_file).unwrap();
child.kill().unwrap();
}
// Sets up omni-node to run a text exercise based on a chain spec.
async fn omni_node_test_setup(chain_spec_path: PathBuf) {
maybe_build_omni_node();
let omni_node = find_release_binary(OMNI_NODE).unwrap();
let mut child = Command::new(omni_node)
.arg("--dev")
.args(["--chain", chain_spec_path.to_str().unwrap()])
.stderr(Stdio::piped())
.spawn()
.unwrap();
let stderr = child.stderr.take().unwrap();
assert_eq!(imported_block_found(stderr, 7, 100).await, true);
child.kill().unwrap();
}
#[tokio::test]
async fn works_with_different_block_times() {
test_runtime_preset(PARA_RUNTIME, 100, Some(DEV_RUNTIME_PRESET.into())).await;
test_runtime_preset(PARA_RUNTIME, 3000, Some(DEV_RUNTIME_PRESET.into())).await;
// we need this snippet just for docs
#[docify::export_content(csb)]
fn build_teyrchain_spec_works() {
let chain_spec_builder = find_release_binary(&CHAIN_SPEC_BUILDER).unwrap();
let runtime_path = find_wasm(PARA_RUNTIME).unwrap();
let output = "/tmp/demo-chain-spec.json";
let runtime_str = runtime_path.to_str().unwrap();
run_cmd!(
$chain_spec_builder -c $output create --relay-chain dontcare -r $runtime_str named-preset development
).expect("Failed to run command");
std::fs::remove_file(output).unwrap();
}
build_teyrchain_spec_works();
}
#[tokio::test]
async fn teyrchain_runtime_works() {
// TODO: None doesn't work. But maybe it should? it would be misleading as many users might
// use it.
for preset in [Some(DEV_RUNTIME_PRESET.into()), Some(LOCAL_TESTNET_RUNTIME_PRESET.into())] {
test_runtime_preset(PARA_RUNTIME, 1000, preset).await;
}
}
#[tokio::test]
async fn omni_node_dev_mode_works() {
//Omni Node in dev mode works with teyrchain's template `dev_chain_spec`
let dev_chain_spec = std::env::current_dir()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("templates")
.join("teyrchain")
.join("dev_chain_spec.json");
omni_node_test_setup(dev_chain_spec).await;
}
#[tokio::test]
// This is a regresion test so that we still remain compatible with runtimes that use
// `para-id` in chain specs, instead of implementing the
// `cumulus_primitives_core::GetTeyrchainInfo`.
async fn omni_node_dev_mode_works_without_getteyrchaininfo() {
let dev_chain_spec = std::env::current_dir()
.unwrap()
.join("src/guides/teyrchain_without_getteyrchaininfo.json");
omni_node_test_setup(dev_chain_spec).await;
}
}
@@ -0,0 +1,789 @@
//! # Currency Pallet
//!
//! By the end of this guide, you will have written a small FRAME pallet (see
//! [`crate::pezkuwi_sdk::frame_runtime`]) that is capable of handling a simple crypto-currency.
//! This pallet will:
//!
//! 1. Allow anyone to mint new tokens into accounts (which is obviously not a great idea for a real
//! system).
//! 2. Allow any user that owns tokens to transfer them to others.
//! 3. Track the total issuance of all tokens at all times.
//!
//! > This guide will build a currency pallet from scratch using only the lowest primitives of
//! > FRAME, and is mainly intended for education, not *applicability*. For example, almost all
//! > FRAME-based runtimes use various techniques to re-use a currency pallet instead of writing
//! > one. Further advanced FRAME related topics are discussed in [`crate::reference_docs`].
//!
//! ## Writing Your First Pallet
//!
//! To get started, clone one of the templates mentioned in [`crate::pezkuwi_sdk::templates`]. We
//! recommend using the `pezkuwi-sdk-minimal-template`. You might need to change small parts of
//! this guide, namely the crate/package names, based on which template you use.
//!
//! > Be aware that you can read the entire source code backing this tutorial by clicking on the
//! > `source` button at the top right of the page.
//!
//! You should have studied the following modules as a prelude to this guide:
//!
//! - [`crate::reference_docs::blockchain_state_machines`]
//! - [`crate::reference_docs::trait_based_programming`]
//! - [`crate::pezkuwi_sdk::frame_runtime`]
//!
//! ## Topics Covered
//!
//! The following FRAME topics are covered in this guide:
//!
//! - [`pallet::storage`]
//! - [`pallet::call`]
//! - [`pallet::event`]
//! - [`pallet::error`]
//! - Basics of testing a pallet
//! - [Constructing a runtime](frame::runtime::prelude::construct_runtime)
//!
//! ### Shell Pallet
//!
//! Consider the following as a "shell pallet". We continue building the rest of this pallet based
//! on this template.
//!
//! [`pallet::config`] and [`pallet::pallet`] are both mandatory parts of any
//! pallet. Refer to the documentation of each to get an overview of what they do.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", shell_pallet)]
//!
//! All of the code that follows in this guide should live inside of the `mod pallet`.
//!
//! ### Storage
//!
//! First, we will need to create two onchain storage declarations.
//!
//! One should be a mapping from account-ids to a balance type, and one value that is the total
//! issuance.
//!
//! > For the rest of this guide, we will opt for a balance type of `u128`. For the sake of
//! > simplicity, we are hardcoding this type. In a real pallet is best practice to define it as a
//! > generic bounded type in the `Config` trait, and then specify it in the implementation.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balance)]
//!
//! The definition of these two storage items, based on [`pallet::storage`] details, is as follows:
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", TotalIssuance)]
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Balances)]
//!
//! ### Dispatchables
//!
//! Next, we will define the dispatchable functions. As per [`pallet::call`], these will be defined
//! as normal `fn`s attached to `struct Pallet`.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_pallet)]
//!
//! The logic of these functions is self-explanatory. Instead, we will focus on the FRAME-related
//! details:
//!
//! - Where do `T::AccountId` and `T::RuntimeOrigin` come from? These are both defined in
//! [`frame::prelude::frame_system::Config`], therefore we can access them in `T`.
//! - What is `ensure_signed`, and what does it do with the aforementioned `T::RuntimeOrigin`? This
//! is outside the scope of this guide, and you can learn more about it in the origin reference
//! document ([`crate::reference_docs::frame_origin`]). For now, you should only know the
//! signature of the function: it takes a generic `T::RuntimeOrigin` and returns a
//! `Result<T::AccountId, _>`. So by the end of this function call, we know that this dispatchable
//! was signed by `sender`.
#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", ensure_signed)]
//!
//! - Where does `mutate`, `get` and `insert` and other storage APIs come from? All of them are
//! explained in the corresponding `type`, for example, for `Balances::<T>::insert`, you can look
//! into [`frame::prelude::StorageMap::insert`].
//!
//! - The return type of all dispatchable functions is [`frame::prelude::DispatchResult`]:
#![doc = docify::embed!("../../substrate/frame/support/src/dispatch.rs", DispatchResult)]
//!
//! Which is more or less a normal Rust `Result`, with a custom [`frame::prelude::DispatchError`] as
//! the `Err` variant. We won't cover this error in detail here, but importantly you should know
//! that there is an `impl From<&'static string> for DispatchError` provided (see
//! [here](`frame::prelude::DispatchError#impl-From<%26str>-for-DispatchError`)). Therefore,
//! we can use basic string literals as our error type and `.into()` them into `DispatchError`.
//!
//! - Why are all `get` and `mutate` functions returning an `Option`? This is the default behavior
//! of FRAME storage APIs. You can learn more about how to override this by looking into
//! [`pallet::storage`], and [`frame::prelude::ValueQuery`]/[`frame::prelude::OptionQuery`]
//!
//! ### Improving Errors
//!
//! How we handle error in the above snippets is fairly rudimentary. Let's look at how this can be
//! improved. First, we can use [`frame::prelude::ensure`] to express the error slightly better.
//! This macro will call `.into()` under the hood.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better)]
//!
//! Moreover, you will learn in the [Defensive Programming
//! section](crate::reference_docs::defensive_programming) that it is always recommended to use
//! safe arithmetic operations in your runtime. By using [`frame::traits::CheckedSub`], we can not
//! only take a step in that direction, but also improve the error handing and make it slightly more
//! ergonomic.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_better_checked)]
//!
//! This is more or less all the logic that there is in this basic currency pallet!
//!
//! ### Your First (Test) Runtime
//!
//! The typical testing code of a pallet lives in a module that imports some preludes useful for
//! testing, similar to:
//!
//! ```
//! pub mod pallet {
//! // snip -- actually pallet code.
//! }
//!
//! #[cfg(test)]
//! mod tests {
//! // bring in the testing prelude of frame
//! use frame::testing_prelude::*;
//! // bring in all pallet items
//! use super::pallet::*;
//!
//! // snip -- rest of the testing code.
//! }
//! ```
//!
//! Next, we create a "test runtime" in order to test our pallet. Recall from
//! [`crate::pezkuwi_sdk::frame_runtime`] that a runtime is a collection of pallets, expressed
//! through [`frame::runtime::prelude::construct_runtime`]. All runtimes also have to include
//! [`frame::prelude::frame_system`]. So we expect to see a runtime with two pallet, `frame_system`
//! and the one we just wrote.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime)]
//!
//! > [`frame::pallet_macros::derive_impl`] is a FRAME feature that enables developers to have
//! > defaults for associated types.
//!
//! Recall that within our pallet, (almost) all blocks of code are generic over `<T: Config>`. And,
//! because `trait Config: frame_system::Config`, we can get access to all items in `Config` (or
//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how
//! Rust traits and generics work. If unfamiliar with this pattern, read
//! [`crate::reference_docs::trait_based_programming`] before going further.
//!
//! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct`
//! is to implement the `trait Config` of all pallets. That is, anywhere within your pallet code
//! where you see `<T: Config>` (read: *"some type `T` that implements `Config`"*), in the runtime,
//! it can be replaced with `<Runtime>`, because `Runtime` implements `Config` of all pallets, as we
//! see above.
//!
//! Another way to think about this is that within a pallet, a lot of types are "unknown" and, we
//! only know that they will be provided at some later point. For example, when you write
//! `T::AccountId` (which is short for `<T as frame_system::Config>::AccountId`) in your pallet,
//! you are in fact saying "*Some type `AccountId` that will be known later*". That "later" is in
//! fact when you specify these types when you implement all `Config` traits for `Runtime`.
//!
//! As you see above, `frame_system::Config` is setting the `AccountId` to `u64`. Of course, a real
//! runtime will not use this type, and instead reside to a proper type like a 32-byte standard
//! public key. This is a HUGE benefit that FRAME developers can tap into: through the framework
//! being so generic, different types can always be customized to simple things when needed.
//!
//! > Imagine how hard it would have been if all tests had to use a real 32-byte account id, as
//! > opposed to just a u64 number 🙈.
//!
//! ### Your First Test
//!
//! The above is all you need to execute the dispatchables of your pallet. The last thing you need
//! to learn is that all of your pallet testing code should be wrapped in
//! [`frame::testing_prelude::TestState`]. This is a type that provides access to an in-memory state
//! to be used in our tests.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", first_test)]
//!
//! In the first test, we simply assert that there is no total issuance, and no balance associated
//! with Alice's account. Then, we mint some balance into Alice's, and re-check.
//!
//! As noted above, the `T::AccountId` is now `u64`. Moreover, `Runtime` is replacing `<T: Config>`.
//! This is why for example you see `Balances::<Runtime>::get(..)`. Finally, notice that the
//! dispatchables are simply functions that can be called on top of the `Pallet` struct.
//!
//! Congratulations! You have written your first pallet and tested it! Next, we learn a few optional
//! steps to improve our pallet.
//!
//! ## Improving the Currency Pallet
//!
//! ### Better Test Setup
//!
//! Idiomatic FRAME pallets often use Builder pattern to define their initial state.
//!
//! > The Pezkuwi Blockchain Academy's Rust entrance exam has a
//! > [section](https://github.com/pezkuwichain/kurdistan_blockchain-akademy/blob/main/src/m_builder.rs)
//! > on this that you can use to learn the Builder Pattern.
//!
//! Let's see how we can implement a better test setup using this pattern. First, we define a
//! `struct StateBuilder`.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", StateBuilder)]
//!
//! This struct is meant to contain the same list of accounts and balances that we want to have at
//! the beginning of each block. We hardcoded this to `let accounts = vec![(ALICE, 100), (2, 100)];`
//! so far. Then, if desired, we attach a default value for this struct.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", default_state_builder)]
//!
//! Like any other builder pattern, we attach functions to the type to mutate its internal
//! properties.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_add)]
//!
//! Finally --the useful part-- we write our own custom `build_and_execute` function on
//! this type. This function will do multiple things:
//!
//! 1. It would consume `self` to produce our `TestState` based on the properties that we attached
//! to `self`.
//! 2. It would execute any test function that we pass in as closure.
//! 3. A nifty trick, this allows our test setup to have some code that is executed both before and
//! after each test. For example, in this test, we do some additional checking about the
//! correctness of the `TotalIssuance`. We leave it up to you as an exercise to learn why the
//! assertion should always hold, and how it is checked.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", impl_state_builder_build)]
//!
//! We can write tests that specifically check the initial state, and making sure our `StateBuilder`
//! is working exactly as intended.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_works)]
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", state_builder_add_balance)]
//!
//! ### More Tests
//!
//! Now that we have a more ergonomic test setup, let's see how a well written test for transfer and
//! mint would look like.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_works)]
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", mint_works)]
//!
//! It is always a good idea to build a mental model where you write *at least* one test for each
//! "success path" of a dispatchable, and one test for each "failure path", such as:
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_from_non_existent_fails)]
//!
//! We leave it up to you to write a test that triggers the `InsufficientBalance` error.
//!
//! ### Event and Error
//!
//! Our pallet is mainly missing two parts that are common in most FRAME pallets: Events, and
//! Errors. First, let's understand what each is.
//!
//! - **Error**: The static string-based error scheme we used so far is good for readability, but it
//! has a few drawbacks. The biggest problem with strings are that they are not type safe, e.g. a
//! match statement cannot be exhaustive. These string literals will bloat the final wasm blob,
//! and are relatively heavy to transmit and encode/decode. Moreover, it is easy to mistype them
//! by one character. FRAME errors are exactly a solution to maintain readability, whilst fixing
//! the drawbacks mentioned. In short, we use an enum to represent different variants of our
//! error. These variants are then mapped in an efficient way (using only `u8` indices) to
//! [`sp_runtime::DispatchError::Module`]. Read more about this in [`pallet::error`].
//!
//! - **Event**: Events are akin to the return type of dispatchables. They are mostly data blobs
//! emitted by the runtime to let outside world know what is happening inside the pallet. Since
//! otherwise, the outside world does not have an easy access to the state changes. They should
//! represent what happened at the end of a dispatch operation. Therefore, the convention is to
//! use passive tense for event names (eg. `SomethingHappened`). This allows other sub-systems or
//! external parties (eg. a light-node, a DApp) to listen to particular events happening, without
//! needing to re-execute the whole state transition function.
//!
//! With the explanation out of the way, let's see how these components can be added. Both follow a
//! fairly familiar syntax: normal Rust enums, with extra [`pallet::event`] and [`pallet::error`]
//! attributes attached.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Event)]
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", Error)]
//!
//! One slightly custom part of this is the [`pallet::generate_deposit`] part. Without going into
//! too much detail, in order for a pallet to emit events to the rest of the system, it needs to do
//! two things:
//!
//! 1. Declare a type in its `Config` that refers to the overarching event type of the runtime. In
//! short, by doing this, the pallet is expressing an important bound: `type RuntimeEvent:
//! From<Event<Self>>`. Read: a `RuntimeEvent` exists, and it can be created from the local `enum
//! Event` of this pallet. This enables the pallet to convert its `Event` into `RuntimeEvent`, and
//! store it where needed.
//!
//! 2. But, doing this conversion and storing is too much to expect each pallet to define. FRAME
//! provides a default way of storing events, and this is what [`pallet::generate_deposit`] is
//! doing.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", config_v2)]
//!
//! > These `Runtime*` types are better explained in
//! > [`crate::reference_docs::frame_runtime_types`].
//!
//! Then, we can rewrite the `transfer` dispatchable as such:
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", transfer_v2)]
//!
//! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime
//! setup.
#![doc = docify::embed!("./packages/guides/first-pallet/src/lib.rs", runtime_v2)]
//!
//! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent =
//! RuntimeEvent`) is generated by
//! [`construct_runtime`](frame::runtime::prelude::construct_runtime). An interesting way to inspect
//! this type is to see its definition in rust-docs:
//! [`crate::guides::your_first_pallet::pallet_v2::tests::runtime_v2::RuntimeEvent`].
//!
//!
//! ## What Next?
//!
//! The following topics where used in this guide, but not covered in depth. It is suggested to
//! study them subsequently:
//!
//! - [`crate::reference_docs::defensive_programming`].
//! - [`crate::reference_docs::frame_origin`].
//! - [`crate::reference_docs::frame_runtime_types`].
//! - The pallet we wrote in this guide was using `dev_mode`, learn more in [`pallet::config`].
//! - Learn more about the individual pallet items/macros, such as event and errors and call, in
//! [`frame::pallet_macros`].
//!
//! [`pallet::storage`]: frame_support::pallet_macros::storage
//! [`pallet::call`]: frame_support::pallet_macros::call
//! [`pallet::event`]: frame_support::pallet_macros::event
//! [`pallet::error`]: frame_support::pallet_macros::error
//! [`pallet::pallet`]: frame_support::pallet
//! [`pallet::config`]: frame_support::pallet_macros::config
//! [`pallet::generate_deposit`]: frame_support::pallet_macros::generate_deposit
#[docify::export]
#[frame::pallet(dev_mode)]
pub mod shell_pallet {
use frame::prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
#[frame::pallet(dev_mode)]
pub mod pallet {
use frame::prelude::*;
#[docify::export]
pub type Balance = u128;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[docify::export]
/// Single storage item, of type `Balance`.
#[pallet::storage]
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
#[docify::export]
/// A mapping from `T::AccountId` to `Balance`
#[pallet::storage]
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
#[docify::export(impl_pallet)]
#[pallet::call]
impl<T: Config> Pallet<T> {
/// An unsafe mint that can be called by anyone. Not a great idea.
pub fn mint_unsafe(
origin: T::RuntimeOrigin,
dest: T::AccountId,
amount: Balance,
) -> DispatchResult {
// ensure that this is a signed account, but we don't really check `_anyone`.
let _anyone = ensure_signed(origin)?;
// update the balances map. Notice how all `<T: Config>` remains as `<T>`.
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
// update total issuance.
TotalIssuance::<T>::mutate(|t| *t = Some(t.unwrap_or(0) + amount));
Ok(())
}
/// Transfer `amount` from `origin` to `dest`.
pub fn transfer(
origin: T::RuntimeOrigin,
dest: T::AccountId,
amount: Balance,
) -> DispatchResult {
let sender = ensure_signed(origin)?;
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
if sender_balance < amount {
return Err("InsufficientBalance".into());
}
let remainder = sender_balance - amount;
// update sender and dest balances.
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
Balances::<T>::insert(&sender, remainder);
Ok(())
}
}
#[allow(unused)]
impl<T: Config> Pallet<T> {
#[docify::export]
pub fn transfer_better(
origin: T::RuntimeOrigin,
dest: T::AccountId,
amount: Balance,
) -> DispatchResult {
let sender = ensure_signed(origin)?;
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
ensure!(sender_balance >= amount, "InsufficientBalance");
let remainder = sender_balance - amount;
// .. snip
Ok(())
}
#[docify::export]
/// Transfer `amount` from `origin` to `dest`.
pub fn transfer_better_checked(
origin: T::RuntimeOrigin,
dest: T::AccountId,
amount: Balance,
) -> DispatchResult {
let sender = ensure_signed(origin)?;
let sender_balance = Balances::<T>::get(&sender).ok_or("NonExistentAccount")?;
let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?;
// .. snip
Ok(())
}
}
#[cfg(any(test, doc))]
pub(crate) mod tests {
use crate::guides::your_first_pallet::pallet::*;
#[docify::export(testing_prelude)]
use frame::testing_prelude::*;
pub(crate) const ALICE: u64 = 1;
pub(crate) const BOB: u64 = 2;
pub(crate) const CHARLIE: u64 = 3;
#[docify::export]
// This runtime is only used for testing, so it should be somewhere like `#[cfg(test)] mod
// tests { .. }`
mod runtime {
use super::*;
// we need to reference our `mod pallet` as an identifier to pass to
// `construct_runtime`.
// YOU HAVE TO CHANGE THIS LINE BASED ON YOUR TEMPLATE
use crate::guides::your_first_pallet::pallet as pallet_currency;
construct_runtime!(
pub enum Runtime {
// ---^^^^^^ This is where `enum Runtime` is defined.
System: frame_system,
Currency: pallet_currency,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Runtime>;
// within pallet we just said `<T as frame_system::Config>::AccountId`, now we
// finally specified it.
type AccountId = u64;
}
// our simple pallet has nothing to be configured.
impl pallet_currency::Config for Runtime {}
}
pub(crate) use runtime::*;
#[allow(unused)]
#[docify::export]
fn new_test_state_basic() -> TestState {
let mut state = TestState::new_empty();
let accounts = vec![(ALICE, 100), (BOB, 100)];
state.execute_with(|| {
for (who, amount) in &accounts {
Balances::<Runtime>::insert(who, amount);
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
}
});
state
}
#[docify::export]
pub(crate) struct StateBuilder {
balances: Vec<(<Runtime as frame_system::Config>::AccountId, Balance)>,
}
#[docify::export(default_state_builder)]
impl Default for StateBuilder {
fn default() -> Self {
Self { balances: vec![(ALICE, 100), (BOB, 100)] }
}
}
#[docify::export(impl_state_builder_add)]
impl StateBuilder {
fn add_balance(
mut self,
who: <Runtime as frame_system::Config>::AccountId,
amount: Balance,
) -> Self {
self.balances.push((who, amount));
self
}
}
#[docify::export(impl_state_builder_build)]
impl StateBuilder {
pub(crate) fn build_and_execute(self, test: impl FnOnce() -> ()) {
let mut ext = TestState::new_empty();
ext.execute_with(|| {
for (who, amount) in &self.balances {
Balances::<Runtime>::insert(who, amount);
TotalIssuance::<Runtime>::mutate(|b| *b = Some(b.unwrap_or(0) + amount));
}
});
ext.execute_with(test);
// assertions that must always hold
ext.execute_with(|| {
assert_eq!(
Balances::<Runtime>::iter().map(|(_, x)| x).sum::<u128>(),
TotalIssuance::<Runtime>::get().unwrap_or_default()
);
})
}
}
#[docify::export]
#[test]
fn first_test() {
TestState::new_empty().execute_with(|| {
// We expect Alice's account to have no funds.
assert_eq!(Balances::<Runtime>::get(&ALICE), None);
assert_eq!(TotalIssuance::<Runtime>::get(), None);
// mint some funds into Alice's account.
assert_ok!(Pallet::<Runtime>::mint_unsafe(
RuntimeOrigin::signed(ALICE),
ALICE,
100
));
// re-check the above
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
assert_eq!(TotalIssuance::<Runtime>::get(), Some(100));
})
}
#[docify::export]
#[test]
fn state_builder_works() {
StateBuilder::default().build_and_execute(|| {
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
});
}
#[docify::export]
#[test]
fn state_builder_add_balance() {
StateBuilder::default().add_balance(CHARLIE, 42).build_and_execute(|| {
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(42));
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
})
}
#[test]
#[should_panic]
fn state_builder_duplicate_genesis_fails() {
StateBuilder::default()
.add_balance(CHARLIE, 42)
.add_balance(CHARLIE, 43)
.build_and_execute(|| {
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
})
}
#[docify::export]
#[test]
fn mint_works() {
StateBuilder::default().build_and_execute(|| {
// given the initial state, when:
assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(ALICE), BOB, 100));
// then:
assert_eq!(Balances::<Runtime>::get(&BOB), Some(200));
assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
// given:
assert_ok!(Pallet::<Runtime>::mint_unsafe(
RuntimeOrigin::signed(ALICE),
CHARLIE,
100
));
// then:
assert_eq!(Balances::<Runtime>::get(&CHARLIE), Some(100));
assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
});
}
#[docify::export]
#[test]
fn transfer_works() {
StateBuilder::default().build_and_execute(|| {
// given the initial state, when:
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
// then:
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
// when:
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(BOB), ALICE, 50));
// then:
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
});
}
#[docify::export]
#[test]
fn transfer_from_non_existent_fails() {
StateBuilder::default().build_and_execute(|| {
// given the initial state, when:
assert_err!(
Pallet::<Runtime>::transfer(RuntimeOrigin::signed(CHARLIE), ALICE, 10),
"NonExistentAccount"
);
// then nothing has changed.
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(100));
assert_eq!(Balances::<Runtime>::get(&BOB), Some(100));
assert_eq!(Balances::<Runtime>::get(&CHARLIE), None);
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
});
}
}
}
#[frame::pallet(dev_mode)]
pub mod pallet_v2 {
use super::pallet::Balance;
use frame::prelude::*;
#[docify::export(config_v2)]
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type of the runtime.
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>
+ TryInto<Event<Self>>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::storage]
pub type Balances<T: Config> = StorageMap<_, _, T::AccountId, Balance>;
#[pallet::storage]
pub type TotalIssuance<T: Config> = StorageValue<_, Balance>;
#[docify::export]
#[pallet::error]
pub enum Error<T> {
/// Account does not exist.
NonExistentAccount,
/// Account does not have enough balance.
InsufficientBalance,
}
#[docify::export]
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A transfer succeeded.
Transferred { from: T::AccountId, to: T::AccountId, amount: Balance },
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[docify::export(transfer_v2)]
pub fn transfer(
origin: T::RuntimeOrigin,
dest: T::AccountId,
amount: Balance,
) -> DispatchResult {
let sender = ensure_signed(origin)?;
// ensure sender has enough balance, and if so, calculate what is left after `amount`.
let sender_balance =
Balances::<T>::get(&sender).ok_or(Error::<T>::NonExistentAccount)?;
let remainder =
sender_balance.checked_sub(amount).ok_or(Error::<T>::InsufficientBalance)?;
Balances::<T>::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount));
Balances::<T>::insert(&sender, remainder);
Self::deposit_event(Event::<T>::Transferred { from: sender, to: dest, amount });
Ok(())
}
}
#[cfg(any(test, doc))]
pub mod tests {
use super::{super::pallet::tests::StateBuilder, *};
use frame::testing_prelude::*;
const ALICE: u64 = 1;
const BOB: u64 = 2;
#[docify::export]
pub mod runtime_v2 {
use super::*;
use crate::guides::your_first_pallet::pallet_v2 as pallet_currency;
construct_runtime!(
pub enum Runtime {
System: frame_system,
Currency: pallet_currency,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Runtime>;
type AccountId = u64;
}
impl pallet_currency::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
}
pub(crate) use runtime_v2::*;
#[docify::export(transfer_works_v2)]
#[test]
fn transfer_works() {
StateBuilder::default().build_and_execute(|| {
// skip the genesis block, as events are not deposited there and we need them for
// the final assertion.
System::set_block_number(ALICE);
// given the initial state, when:
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(ALICE), BOB, 50));
// then:
assert_eq!(Balances::<Runtime>::get(&ALICE), Some(50));
assert_eq!(Balances::<Runtime>::get(&BOB), Some(150));
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
// now we can also check that an event has been deposited:
assert_eq!(
System::read_events_for_pallet::<Event<Runtime>>(),
vec![Event::Transferred { from: ALICE, to: BOB, amount: 50 }]
);
});
}
}
}
@@ -0,0 +1,186 @@
//! # Your first Runtime
//!
//! This guide will walk you through the steps to add your pallet to a runtime.
//!
//! The good news is, in [`crate::guides::your_first_pallet`], we have already created a _test_
//! runtime that was used for testing, and a real runtime is not that much different!
//!
//! ## Setup
//!
//! A runtime shares a few similar setup requirements as with a pallet:
//!
//! * importing [`frame`], [`codec`], and [`scale_info`] crates.
//! * following the [`std` feature-gating](crate::pezkuwi_sdk::substrate#wasm-build) pattern.
//!
//! But, more specifically, it also contains:
//!
//! * a `build.rs` that uses [`substrate_wasm_builder`]. This entails declaring
//! `[build-dependencies]` in the Cargo manifest file:
//!
//! ```ignore
//! [build-dependencies]
//! substrate-wasm-builder = { ... }
//! ```
//!
//! >Note that a runtime must always be one-runtime-per-crate. You cannot define multiple runtimes
//! per rust crate.
//!
//! You can find the full code of this guide in [`first_runtime`].
//!
//! ## Your First Runtime
//!
//! The first new property of a real runtime that it must define its
//! [`frame::runtime::prelude::RuntimeVersion`]:
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", VERSION)]
//!
//! The version contains a number of very important fields, such as `spec_version` and `spec_name`
//! that play an important role in identifying your runtime and its version, more importantly in
//! runtime upgrades. More about runtime upgrades in
//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`].
//!
//! Then, a real runtime also contains the `impl` of all individual pallets' `trait Config` for
//! `struct Runtime`, and a [`frame::runtime::prelude::construct_runtime`] macro that amalgamates
//! them all.
//!
//! In the case of our example:
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", our_config_impl)]
//!
//! In this example, we bring in a number of other pallets from [`frame`] into the runtime, each of
//! their `Config` need to be implemented for `struct Runtime`:
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", config_impls)]
//!
//! Notice how we use [`frame::pallet_macros::derive_impl`] to provide "default" configuration items
//! for each pallet. Feel free to dive into the definition of each default prelude (eg.
//! [`frame::prelude::frame_system::pallet::config_preludes`]) to learn more which types are exactly
//! used.
//!
//! Recall that in test runtime in [`crate::guides::your_first_pallet`], we provided `type AccountId
//! = u64` to `frame_system`, while in this case we rely on whatever is provided by
//! [`SolochainDefaultConfig`], which is indeed a "real" 32 byte account id.
//!
//! Then, a familiar instance of `construct_runtime` amalgamates all of the pallets:
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", cr)]
//!
//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that every (real) runtime needs to
//! implement a set of runtime APIs that will then let the node to communicate with it. The final
//! steps of crafting a runtime are related to achieving exactly this.
//!
//! First, we define a number of types that eventually lead to the creation of an instance of
//! [`frame::runtime::prelude::Executive`]. The executive is a handy FRAME utility that, through
//! amalgamating all pallets and further types, implements some of the very very core pieces of the
//! runtime logic, such as how blocks are executed and other runtime-api implementations.
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", runtime_types)]
//!
//! Finally, we use [`frame::runtime::prelude::impl_runtime_apis`] to implement all of the runtime
//! APIs that the runtime wishes to expose. As you will see in the code, most of these runtime API
//! implementations are merely forwarding calls to `RuntimeExecutive` which handles the actual
//! logic. Given that the implementation block is somewhat large, we won't repeat it here. You can
//! look for `impl_runtime_apis!` in [`first_runtime`].
//!
//! ```ignore
//! impl_runtime_apis! {
//! impl apis::Core<Block> for Runtime {
//! fn version() -> RuntimeVersion {
//! VERSION
//! }
//!
//! fn execute_block(block: Block) {
//! RuntimeExecutive::execute_block(block)
//! }
//!
//! fn initialize_block(header: &Header) -> ExtrinsicInclusionMode {
//! RuntimeExecutive::initialize_block(header)
//! }
//! }
//!
//! // many more trait impls...
//! }
//! ```
//!
//! And that more or less covers the details of how you would write a real runtime!
//!
//! Once you compile a crate that contains a runtime as above, simply running `cargo build` will
//! generate the wasm blobs and place them under `./target/release/wbuild`, as explained
//! [here](crate::pezkuwi_sdk::substrate#wasm-build).
//!
//! ## Genesis Configuration
//!
//! Every runtime specifies a number of runtime APIs that help the outer world (most notably, a
//! `node`) know what is the genesis state of this runtime. These APIs are then used to generate
//! what is known as a **Chain Specification, or chain spec for short**. A chain spec is the
//! primary way to run a new chain.
//!
//! These APIs are defined in [`sp_genesis_builder`], and are re-exposed as a part of
//! [`frame::runtime::apis`]. Therefore, the implementation blocks can be found inside of
//! `impl_runtime_apis!` similar to:
//!
//! ```ignore
//! impl_runtime_apis! {
//! impl apis::GenesisBuilder<Block> for Runtime {
//! fn build_state(config: Vec<u8>) -> GenesisBuilderResult {
//! build_state::<RuntimeGenesisConfig>(config)
//! }
//!
//! fn get_preset(id: &Option<PresetId>) -> Option<Vec<u8>> {
//! get_preset::<RuntimeGenesisConfig>(id, self::genesis_config_presets::get_preset)
//! }
//!
//! fn preset_names() -> Vec<PresetId> {
//! crate::genesis_config_presets::preset_names()
//! }
//! }
//!
//! }
//! ```
//!
//! The implementation of these function can naturally vary from one runtime to the other, but the
//! overall pattern is common. For the case of this runtime, we do the following:
//!
//! 1. Expose one non-default preset, namely [`sp_genesis_builder::DEV_RUNTIME_PRESET`]. This means
//! our runtime has two "presets" of genesis state in total: `DEV_RUNTIME_PRESET` and `None`.
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", preset_names)]
//!
//! For `build_state` and `get_preset`, we use the helper functions provide by frame:
//!
//! * [`frame::runtime::prelude::build_state`] and [`frame::runtime::prelude::get_preset`].
//!
//! Indeed, our runtime needs to specify what its `DEV_RUNTIME_PRESET` genesis state should be like:
#![doc = docify::embed!("./packages/guides/first-runtime/src/lib.rs", development_config_genesis)]
//!
//! For more in-depth information about `GenesisConfig`, `ChainSpec`, the `GenesisBuilder` API and
//! `chain-spec-builder`, see [`crate::reference_docs::chain_spec_genesis`].
//!
//! ## Next Step
//!
//! See [`crate::guides::your_first_node`].
//!
//! ## Further Reading
//!
//! 1. To learn more about signed extensions, see [`crate::reference_docs::signed_extensions`].
//! 2. `AllPalletsWithSystem` is also generated by `construct_runtime`, as explained in
//! [`crate::reference_docs::frame_runtime_types`].
//! 3. `Executive` supports more generics, most notably allowing the runtime to configure more
//! runtime migrations, as explained in
//! [`crate::reference_docs::frame_runtime_upgrades_and_migrations`].
//! 4. Learn more about adding and implementing runtime apis in
//! [`crate::reference_docs::custom_runtime_api_rpc`].
//! 5. To see a complete example of a runtime+pallet that is similar to this guide, please see
//! [`crate::pezkuwi_sdk::templates`].
//!
//! [`SolochainDefaultConfig`]: struct@frame_system::pallet::config_preludes::SolochainDefaultConfig
#[cfg(test)]
mod tests {
use cmd_lib::run_cmd;
const FIRST_RUNTIME: &'static str = "pezkuwi-sdk-docs-first-runtime";
#[docify::export_content]
#[test]
fn build_runtime() {
run_cmd!(
cargo build --release -p $FIRST_RUNTIME
)
.expect("Failed to run command");
}
}
+50
View File
@@ -0,0 +1,50 @@
//! # Pezkuwi SDK Docs
//!
//! The Pezkuwi SDK Developer Documentation.
//!
//! This crate is a *minimal*, *always-accurate* and low level source of truth about Pezkuwi-SDK.
//! For more high level docs, please go to [docs.pezkuwi.com](https://docs.pezkuwichain.io).
//!
//! ## Getting Started
//!
//! We suggest the following reading sequence:
//!
//! - Start by learning about [`pezkuwi_sdk`], its structure and context.
//! - Then, head over to the [`guides`]. This modules contains in-depth guides about the most
//! important user-journeys of the Pezkuwi SDK.
//! - Whilst reading the guides, you might find back-links to [`reference_docs`].
//! - [`external_resources`] for a list of 3rd party guides and tutorials.
//! - Finally, <https://paritytech.github.io> is the parent website of this crate that contains the
//! list of further tools related to the Pezkuwi SDK.
//!
//! ## Information Architecture
//!
//! This section paints a picture over the high-level information architecture of this crate.
#![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")]
#![warn(rustdoc::broken_intra_doc_links)]
#![warn(rustdoc::private_intra_doc_links)]
// Frame macros reference features which this crate does not have
#![allow(unexpected_cfgs)]
#![doc(html_favicon_url = "https://pezkuwichain.io/favicon.ico")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_White.png"
)]
#![doc(issue_tracker_base_url = "https://github.com/pezkuwichain/pezkuwi-sdk/issues")]
/// Meta information about this crate, how it is built, what principles dictates its evolution and
/// how one can contribute to it.
pub mod meta_contributing;
/// A list of external resources and learning material about Pezkuwi SDK.
pub mod external_resources;
/// In-depth guides about the most common components of the Pezkuwi SDK. They are slightly more
/// high level and broad than [`reference_docs`].
pub mod guides;
/// An introduction to the Pezkuwi SDK. Read this module to learn about the structure of the SDK,
/// the tools that are provided as a part of it, and to gain a high level understanding of each.
pub mod pezkuwi_sdk;
/// Reference documents covering in-depth topics across the Pezkuwi SDK. It is suggested to read
/// these on-demand, while you are going through the [`guides`] or other content.
pub mod reference_docs;
@@ -0,0 +1,151 @@
//! # Contribution
//!
//! The following sections cover more detailed information about this crate and how it should be
//! maintained.
//!
//! ## Why Rust Docs?
//!
//! We acknowledge that blockchain based systems, particularly a cutting-edge one like Pezkuwi SDK
//! is a software artifact that is complex, and rapidly evolving. This makes the task of documenting
//! it externally extremely difficult, especially with regards to making sure it is up-to-date.
//!
//! Consequently, we argue that the best hedge against this is to move as much of the documentation
//! near the source code as possible. This would further incentivize developers to keep the
//! documentation up-to-date, as the overhead is reduced by making sure everything is in one
//! repository, and everything being in `.rs` files.
//!
//! > This is not to say that a more visually appealing version of this crate (for example as an
//! > `md-book`) cannot exist, but it would be outside the scope of this crate.
//!
//! Moreover, we acknowledge that a major pain point has been not only outdated *concepts*, but also
//! *outdated code*. For this, we commit to making sure no code-snippet in this crate is left as
//! `///ignore` or `///no_compile`, making sure all code snippets are self-contained, compile-able,
//! and correct at every single revision of the entire repository.
//!
//! > This also allows us to have a clear versioning on the entire content of this crate. For every
//! commit of the Pezkuwi SDK, there would be one version of this crate that is guaranteed to be
//! correct.
//!
//! > To achieve this, we often use [`docify`](https://github.com/sam0x17/docify), a nifty invention
//! > of `@sam0x17`.
//!
//! Also see: <https://github.com/pezkuwichain/pezkuwi-sdk/issues/109>.
//!
//! ## Scope
//!
//! The above would NOT be attainable if we don't acknowledge that the scope of this crate MUST be
//! limited, or else its maintenance burden would be infeasible or not worthwhile. In short, future
//! maintainers should always strive to keep the content of this repository as minimal as possible.
//! Some of the following principles are specifically there to be the guidance for this.
//!
//! ## Principles
//!
//! The following guidelines are meant to be the guiding torch of those who contribute to this
//! crate.
//!
//! 1. 🔺 Ground Up: Information should be laid out in the most ground-up fashion. The lowest level
//! (i.e. "ground") is Rust-docs. The highest level (i.e. "up") is "outside of this crate". In
//! between lies [`reference_docs`] and [`guides`], from low to high. The point of this principle
//! is to document as much of the information as possible in the lower level media, as it is
//! easier to maintain and more reachable. Then, use excessive linking to back-link when writing
//! in a more high level.
//!
//! > A prime example of this, the details of the FRAME storage APIs should NOT be explained in a
//! > high level tutorial. They should be explained in the rust-doc of the corresponding type or
//! > macro.
//!
//! 2. 🧘 Less is More: For reasons mentioned [above](#why-rust-docs), the more concise this crate
//! is, the better.
//! 3. √ Dont Repeat Yourself DRY: A summary of the above two points. Authors should always
//! strive to avoid any duplicate information. Every concept should ideally be documented in
//! *ONE* place and one place only. This makes the task of maintaining topics significantly
//! easier.
//!
//! > A prime example of this, the list of CLI arguments of a particular binary should not be
//! > documented in multiple places across this crate. It should be only be documented in the
//! > corresponding crate (e.g. `sc_cli`).
//!
//! > Moreover, this means that as a contributor, **it is your responsibility to have a grasp over
//! > what topics are already covered in this crate, and how you can build on top of the information
//! > that they already pose, rather than repeating yourself**.
//!
//! For more details see the [latest documenting
//! guidelines](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/DOCUMENTATION_GUIDELINES.md).
//!
//! #### Example: Explaining `#[pallet::call]`
//!
//! <details>
//! <summary>
//! Let's consider the seemingly simple example of explaining to someone dead-simple code of a FRAME
//! call and see how we can use the above principles.
//! </summary>
//!
//!
//! ```
//! #[frame::pallet(dev_mode)]
//! pub mod pallet {
//! # use frame::prelude::*;
//! # #[pallet::config]
//! # pub trait Config: frame_system::Config {}
//! # #[pallet::pallet]
//! # pub struct Pallet<T>(_);
//! #[pallet::call]
//! impl<T: Config> Pallet<T> {
//! pub fn a_simple_call(origin: OriginFor<T>, data: u32) -> DispatchResult {
//! ensure!(data > 10, "SomeStaticString");
//! todo!();
//! }
//! }
//! }
//! ```
//!
//! * Before even getting started, what is with all of this `<T: Config>`? We link to
//! [`crate::reference_docs::trait_based_programming`].
//! * First, the name. Why is this called `pallet::call`? This goes back to `enum Call`, which is
//! explained in [`crate::reference_docs::frame_runtime_types`]. Build on top of this!
//! * Then, what is `origin`? Just an account id? [`crate::reference_docs::frame_origin`].
//! * Then, what is `DispatchResult`? Why is this called *dispatch*? Probably something that can be
//! explained in the documentation of [`frame::prelude::DispatchResult`].
//! * Why is `"SomeStaticString"` a valid error? Because there is implementation for it that you can
//! see [here](frame::prelude::DispatchError#impl-From<%26'static+str>-for-DispatchError).
//!
//!
//! All of these are examples of underlying information that a contributor should:
//!
//! 1. Try and create and they are going along.
//! 2. Back-link to if they already exist.
//!
//! Of course, all of this is not set in stone as a either/or rule. Sometimes, it is necessary to
//! rephrase a concept in a new context.
//!
//! </details>
//!
//! ## `crates.io` and Publishing
//!
//! As it stands now, this crate cannot be published to crates.io because of its use of
//! [workspace-level `docify`](https://github.com/sam0x17/docify/issues/22). For now, we accept this
//! compromise, but in the long term, we should work towards finding a way to maintain different
//! revisions of this crate.
//!
//! ## Versioning
//!
//! So long as not deployed in `crates.io`, please notice that all of the information in this crate,
//! namely in [`crate::guides`] and such are compatible with the master branch of `pezkuwi-sdk`. A
//! few solutions have been proposed to improve this, please see
//! [here](https://github.com/pezkuwichain/pezkuwi-sdk/issues/146).
//!
//! ## How to Develop Locally
//!
//! To view the docs specific [`crate`] locally for development, including the correct HTML headers
//! injected, run:
//!
//! ```sh
//! SKIP_WASM_BUILD=1 \
//! RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/assets/header.html --extend-css $(pwd)/docs/sdk/assets/theme.css --default-theme=ayu" \
//! cargo doc -p pezkuwi-sdk-docs --no-deps --open
//! ```
//!
//! If even faster build time for docs is needed, you can temporarily remove most of the
//! substrate/cumulus dependencies that are only used for linking purposes.
//!
//! For more on local development, see [`crate::reference_docs::development_environment_advice`].
@@ -0,0 +1,130 @@
//! # Cumulus
//!
//! Substrate provides a framework ([FRAME]) through which a blockchain node and runtime can easily
//! be created. Cumulus aims to extend the same approach to creation of Pezkuwi teyrchains.
//!
//! > Cumulus clouds are shaped sort of like dots; together they form a system that is intricate,
//! > beautiful and functional.
//!
//! ## Example: Runtime
//!
//! A Cumulus-based runtime is fairly similar to other [FRAME]-based runtimes. Most notably, the
//! following changes are applied to a normal FRAME-based runtime to make it a Cumulus-based
//! runtime:
//!
//! #### Cumulus Pallets
//!
//! A teyrchain runtime should use a number of pallets that are provided by Cumulus and Substrate.
//! Notably:
//!
//! - [`frame-system`](frame::prelude::frame_system), like all FRAME-based runtimes.
//! - [`cumulus_pallet_teyrchain_system`]
//! - [`teyrchain_info`]
#![doc = docify::embed!("./src/pezkuwi_sdk/cumulus.rs", system_pallets)]
//!
//! Given that all Cumulus-based runtimes use a simple Aura-based consensus mechanism, the following
//! pallets also need to be added:
//!
//! - [`pallet_timestamp`]
//! - [`pallet_aura`]
//! - [`cumulus_pallet_aura_ext`]
#![doc = docify::embed!("./src/pezkuwi_sdk/cumulus.rs", consensus_pallets)]
//!
//!
//! Finally, a separate macro, similar to
//! [`impl_runtime_api`](frame::runtime::prelude::impl_runtime_apis), which creates the default set
//! of runtime APIs, will generate the teyrchain runtime's validation runtime API, also known as
//! teyrchain validation function (PVF). Without this API, the relay chain is unable to validate
//! blocks produced by our teyrchain.
#![doc = docify::embed!("./src/pezkuwi_sdk/cumulus.rs", validate_block)]
//!
//! ---
//!
//! [FRAME]: crate::pezkuwi_sdk::frame_runtime
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#[cfg(test)]
mod tests {
mod runtime {
pub use frame::{
deps::sp_consensus_aura::sr25519::AuthorityId as AuraId, prelude::*,
runtime::prelude::*, testing_prelude::*,
};
#[docify::export(CR)]
construct_runtime!(
pub enum Runtime {
// system-level pallets.
System: frame_system,
Timestamp: pallet_timestamp,
TeyrchainSystem: cumulus_pallet_teyrchain_system,
TeyrchainInfo: teyrchain_info,
// teyrchain consensus support -- mandatory.
Aura: pallet_aura,
AuraExt: cumulus_pallet_aura_ext,
}
);
#[docify::export]
mod system_pallets {
use super::*;
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Self>;
type OnSetCode = cumulus_pallet_teyrchain_system::TeyrchainSetCode<Self>;
}
impl cumulus_pallet_teyrchain_system::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnSystemEvent = ();
type SelfParaId = teyrchain_info::Pallet<Runtime>;
type OutboundXcmpMessageSource = ();
type XcmpMessageHandler = ();
type ReservedDmpWeight = ();
type ReservedXcmpWeight = ();
type CheckAssociatedRelayNumber =
cumulus_pallet_teyrchain_system::RelayNumberMonotonicallyIncreases;
type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook<
Runtime,
6000, // relay chain block time
1,
1,
>;
type WeightInfo = ();
type DmpQueue = frame::traits::EnqueueWithOrigin<(), sp_core::ConstU8<0>>;
type RelayParentOffset = ConstU32<0>;
}
impl teyrchain_info::Config for Runtime {}
}
#[docify::export]
mod consensus_pallets {
use super::*;
impl pallet_aura::Config for Runtime {
type AuthorityId = AuraId;
type DisabledValidators = ();
type MaxAuthorities = ConstU32<100_000>;
type AllowMultipleBlocksPerSlot = ConstBool<false>;
type SlotDuration = pallet_aura::MinimumPeriodTimesTwo<Self>;
}
#[docify::export(timestamp)]
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]
impl pallet_timestamp::Config for Runtime {}
impl cumulus_pallet_aura_ext::Config for Runtime {}
}
#[docify::export(validate_block)]
cumulus_pallet_teyrchain_system::register_validate_block! {
Runtime = Runtime,
BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
}
}
}
@@ -0,0 +1,175 @@
//! # FRAME
//!
//! ```no_compile
//! ______ ______ ________ ___ __ __ ______
//! /_____/\ /_____/\ /_______/\ /__//_//_/\ /_____/\
//! \::::_\/_\:::_ \ \ \::: _ \ \\::\| \| \ \\::::_\/_
//! \:\/___/\\:(_) ) )_\::(_) \ \\:. \ \\:\/___/\
//! \:::._\/ \: __ `\ \\:: __ \ \\:.\-/\ \ \\::___\/_
//! \:\ \ \ \ `\ \ \\:.\ \ \ \\. \ \ \ \\:\____/\
//! \_\/ \_\/ \_\/ \__\/\__\/ \__\/ \__\/ \_____\/
//! ```
//!
//! > **F**ramework for **R**untime **A**ggregation of **M**odularized **E**ntities: Substrate's
//! > State Transition Function (Runtime) Framework.
//!
//! ## Introduction
//!
//! As described in [`crate::reference_docs::wasm_meta_protocol`], at a high-level Substrate-based
//! blockchains are composed of two parts:
//!
//! 1. A *runtime* which represents the state transition function (i.e. "Business Logic") of a
//! blockchain, and is encoded as a WASM blob.
//! 2. A node whose primary purpose is to execute the given runtime.
#![doc = simple_mermaid::mermaid!("../../../mermaid/substrate_simple.mmd")]
//!
//! *FRAME is the Substrate's framework of choice to build a runtime.*
//!
//! FRAME is composed of two major components, **pallets** and a **runtime**.
//!
//! ## Pallets
//!
//! A pallet is a unit of encapsulated logic. It has a clearly defined responsibility and can be
//! linked to other pallets. In order to be reusable, pallets shipped with FRAME strive to only care
//! about its own responsibilities and make as few assumptions about the general runtime as
//! possible. A pallet is analogous to a _module_ in the runtime.
//!
//! A pallet is defined as a `mod pallet` wrapped by the [`frame::pallet`] macro. Within this macro,
//! pallet components/parts can be defined. Most notable of these parts are:
//!
//! - [Config](frame::pallet_macros::config), allowing a pallet to make itself configurable and
//! generic over types, values and such.
//! - [Storage](frame::pallet_macros::storage), allowing a pallet to define onchain storage.
//! - [Dispatchable function](frame::pallet_macros::call), allowing a pallet to define extrinsics
//! that are callable by end users, from the outer world.
//! - [Events](frame::pallet_macros::event), allowing a pallet to emit events.
//! - [Errors](frame::pallet_macros::error), allowing a pallet to emit well-formed errors.
//!
//! Some of these pallet components resemble the building blocks of a smart contract. While both
//! models are programming state transition functions of blockchains, there are crucial differences
//! between the two. See [`crate::reference_docs::runtime_vs_smart_contract`] for more.
//!
//! Most of these components are defined using macros, the full list of which can be found in
//! [`frame::pallet_macros`].
//!
//! ### Example
//!
//! The following example showcases a minimal pallet.
#![doc = docify::embed!("src/pezkuwi_sdk/frame_runtime.rs", pallet)]
//!
//! ## Runtime
//!
//! A runtime is a collection of pallets that are amalgamated together. Each pallet typically has
//! some configurations (exposed as a `trait Config`) that needs to be *specified* in the runtime.
//! This is done with [`frame::runtime::prelude::construct_runtime`].
//!
//! A (real) runtime that actually wishes to compile to WASM needs to also implement a set of
//! runtime-apis. These implementation can be specified using the
//! [`frame::runtime::prelude::impl_runtime_apis`] macro.
//!
//! ### Example
//!
//! The following example shows a (test) runtime that is composing the pallet demonstrated above,
//! next to the [`frame::prelude::frame_system`] pallet, into a runtime.
#![doc = docify::embed!("src/pezkuwi_sdk/frame_runtime.rs", runtime)]
//!
//! ## More Examples
//!
//! You can find more FRAME examples that revolve around specific features at [`pallet_examples`].
//!
//! ## Alternatives 🌈
//!
//! There is nothing in the Substrate's node side code-base that mandates the use of FRAME. While
//! FRAME makes it very simple to write Substrate-based runtimes, it is by no means intended to be
//! the only one. At the end of the day, any WASM blob that exposes the right set of runtime APIs is
//! a valid Runtime form the point of view of a Substrate client (see
//! [`crate::reference_docs::wasm_meta_protocol`]). Notable examples are:
//!
//! * writing a runtime in pure Rust, as done in [this template](https://github.com/JoshOrndorff/frameless-node-template).
//! * writing a runtime in AssemblyScript, as explored in [this project](https://github.com/LimeChain/subsembly).
/// A FRAME based pallet. This `mod` is the entry point for everything else. All
/// `#[pallet::xxx]` macros must be defined in this `mod`. Although, frame also provides an
/// experimental feature to break these parts into different `mod`s. See [`pallet_examples`] for
/// more.
#[docify::export]
#[frame::pallet(dev_mode)]
pub mod pallet {
use frame::prelude::*;
/// The configuration trait of a pallet. Mandatory. Allows a pallet to receive types at a
/// later point from the runtime that wishes to contain it. It allows the pallet to be
/// parameterized over both types and values.
#[pallet::config]
pub trait Config: frame_system::Config {
/// A type that is not known now, but the runtime that will contain this pallet will
/// know it later, therefore we define it here as an associated type.
#[allow(deprecated)]
type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;
/// A parameterize-able value that we receive later via the `Get<_>` trait.
type ValueParameter: Get<u32>;
/// Similar to [`Config::ValueParameter`], but using `const`. Both are functionally
/// equal, but offer different tradeoffs.
const ANOTHER_VALUE_PARAMETER: u32;
}
/// A mandatory struct in each pallet. All functions callable by external users (aka.
/// transactions) must be attached to this type (see [`frame::pallet_macros::call`]). For
/// convenience, internal (private) functions can also be attached to this type.
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);
/// The events that this pallet can emit.
#[pallet::event]
pub enum Event<T: Config> {}
/// A storage item that this pallet contains. This will be part of the state root trie
/// of the blockchain.
#[pallet::storage]
pub type Value<T> = StorageValue<Value = u32>;
/// All *dispatchable* call functions (aka. transactions) are attached to `Pallet` in a
/// `impl` block.
#[pallet::call]
impl<T: Config> Pallet<T> {
/// This will be callable by external users, and has two u32s as a parameter.
pub fn some_dispatchable(
_origin: OriginFor<T>,
_param: u32,
_other_para: u32,
) -> DispatchResult {
Ok(())
}
}
}
/// A simple runtime that contains the above pallet and `frame_system`, the mandatory pallet of
/// all runtimes. This runtime is for testing, but it shares a lot of similarities with a *real*
/// runtime.
#[docify::export]
pub mod runtime {
use super::pallet as pallet_example;
use frame::{prelude::*, testing_prelude::*};
// The major macro that amalgamates pallets into `enum Runtime`
construct_runtime!(
pub enum Runtime {
System: frame_system,
Example: pallet_example,
}
);
// These `impl` blocks specify the parameters of each pallet's `trait Config`.
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Self>;
}
impl pallet_example::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type ValueParameter = ConstU32<42>;
const ANOTHER_VALUE_PARAMETER: u32 = 42;
}
}
+158
View File
@@ -0,0 +1,158 @@
//! # Pezkuwi SDK
//!
//! [Pezkuwi SDK](https://github.com/pezkuwichain/pezkuwi-sdk) provides the main resources needed to
//! start building on the [Pezkuwi network](https://pezkuwichain.io/), a scalable, multi-chain
//! blockchain platform that enables different blockchains to securely interoperate.
//!
//! [![StackExchange](https://img.shields.io/badge/StackExchange-Polkadot%20and%20Substrate-222222?logo=stackexchange)](https://exchange.pezkuwichain.app/)
//!
//! [![awesomeDot](https://img.shields.io/badge/polkadot-awesome-e6007a?logo=polkadot)](https://github.com/Awsmdot/awesome-dot)
//! [![wiki](https://img.shields.io/badge/polkadot-wiki-e6007a?logo=polkadot)](https://wiki.network.pezkuwichain.io/)
//! [![forum](https://img.shields.io/badge/polkadot-forum-e6007a?logo=polkadot)](https://forum.polkadot.network/)
//!
//! [![RFCs](https://img.shields.io/badge/fellowship-RFCs-e6007a?logo=polkadot)](https://github.com/polkadot-fellows/rfcs)
//! [![Runtime](https://img.shields.io/badge/fellowship-runtimes-e6007a?logo=polkadot)](https://github.com/polkadot-fellows/runtimes)
//! [![Manifesto](https://img.shields.io/badge/fellowship-manifesto-e6007a?logo=polkadot)](https://github.com/polkadot-fellows/manifesto/blob/main/manifesto.pdf)
//!
//! ## Getting Started
//!
//! The primary way to get started with the Pezkuwi SDK is to start writing a FRAME-based runtime.
//! See:
//!
//! * [`pezkuwi`], to understand what is Pezkuwi as a development platform.
//! * [`substrate`], for an overview of what Substrate as the main blockchain framework of Pezkuwi
//! SDK.
//! * [`frame`], to learn about how to write blockchain applications aka. "App Chains".
//! * Continue with the [`pezkuwi_sdk_docs`'s "getting started"](crate#getting-started).
//!
//! ## Components
//!
//! #### Substrate
//!
//! [![Substrate-license](https://img.shields.io/badge/License-GPL3%2FApache2.0-blue)](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/substrate/LICENSE-APACHE2)
//! [![GitHub
//! Repo](https://img.shields.io/badge/github-substrate-2324CC85)](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/substrate)
//!
//! [`substrate`] is the base blockchain framework used to power the Pezkuwi SDK. It is a full
//! toolkit to create sovereign blockchains, including but not limited to those which connect to
//! Pezkuwi as teyrchains.
//!
//! #### FRAME
//!
//! [![Substrate-license](https://img.shields.io/badge/License-Apache2.0-blue)](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/substrate/LICENSE-APACHE2)
//! [![GitHub
//! Repo](https://img.shields.io/badge/github-frame-2324CC85)](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/substrate/frame)
//!
//! [`frame`] is the framework used to create Substrate-based application logic, aka. runtimes.
//! Learn more about the distinction of a runtime and node in
//! [`reference_docs::wasm_meta_protocol`].
//!
//! #### Cumulus
//!
//! [![Cumulus-license](https://img.shields.io/badge/License-GPL3-blue)](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/cumulus/LICENSE)
//! [![GitHub
//! Repo](https://img.shields.io/badge/github-cumulus-white)](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/cumulus)
//!
//! [`cumulus`] transforms FRAME-based runtimes into Pezkuwi-compatible teyrchain runtimes, and
//! Substrate-based nodes into Pezkuwi/Teyrchain-compatible nodes.
//!
//! #### XCM
//!
//! [![XCM-license](https://img.shields.io/badge/License-GPL3-blue)](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/LICENSE)
//! [![GitHub
//! Repo](https://img.shields.io/badge/github-XCM-e6007a?logo=polkadot)](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm)
//!
//! [`xcm`], short for "cross consensus message", is the primary format that is used for
//! communication between teyrchains, but is intended to be extensible to other use cases as well.
//!
//! #### Pezkuwi
//!
//! [![Pezkuwi-license](https://img.shields.io/badge/License-GPL3-blue)](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/LICENSE)
//! [![GitHub
//! Repo](https://img.shields.io/badge/github-polkadot-e6007a?logo=polkadot)](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot)
//!
//! [`pezkuwi`] is an implementation of a Pezkuwi node in Rust, by `@paritytech`. The Pezkuwi
//! runtimes are located under the
//! [`pezkuwi-fellows/runtimes`](https://github.com/polkadot-fellows/runtimes) repository.
//!
//! ### Binaries
//!
//! The main binaries that are part of the Pezkuwi SDK are:
//! * [`pezkuwi`]: The Pezkuwi relay chain node binary, as noted above.
//! * [`pezkuwi-omni-node`]: A white-labeled teyrchain collator node. See more in
//! [`crate::reference_docs::omni_node`].
//! * [`pezkuwi-teyrchain-bin`]: The collator node used to run collators for all Pezkuwi system
//! teyrchains.
//! * [`frame-omni-bencher`]: a benchmarking tool for FRAME-based runtimes. Nodes typically contain
//! a
//! `benchmark` subcommand that does the same.
//! * [`chain_spec_builder`]: Utility to build chain-specs Nodes typically contain a `build-spec`
//! subcommand that does the same.
//! * [`subkey`]: Substrate's key management utility.
//! * [`substrate-node`](node_cli) is an extensive substrate node that contains the superset of all
//! runtime and node side features. The corresponding runtime, called [`kitchensink_runtime`]
//! contains all of the modules that are provided with `FRAME`. This node and runtime is only used
//! for testing and demonstration.
//!
//! ### Summary
//!
//! The following diagram summarizes how some of the components of Pezkuwi SDK work together:
#![doc = simple_mermaid::mermaid!("../../../mermaid/pezkuwi_sdk_substrate.mmd")]
//!
//! A Substrate-based chain is a blockchain composed of a runtime and a node. As noted above, the
//! runtime is the application logic of the blockchain, and the node is everything else.
//! See [`reference_docs::wasm_meta_protocol`] for an in-depth explanation of this. The
//! former is built with [`frame`], and the latter is built with rest of Substrate.
//!
//! > You can think of a Substrate-based chain as a white-labeled blockchain.
#![doc = simple_mermaid::mermaid!("../../../mermaid/pezkuwi_sdk_pezkuwi.mmd")]
//! Pezkuwi is itself a Substrate-based chain, composed of the exact same two components. It has
//! specialized logic in both the node and the runtime side, but it is not "special" in any way.
//!
//! A teyrchain is a "special" Substrate-based chain, whereby both the node and the runtime
//! components have became "Pezkuwi-aware" using Cumulus.
#![doc = simple_mermaid::mermaid!("../../../mermaid/pezkuwi_sdk_teyrchain.mmd")]
//!
//! ## Notable Upstream Crates
//!
//! - [`parity-scale-codec`](https://github.com/paritytech/parity-scale-codec)
//! - [`parity-db`](https://github.com/paritytech/parity-db)
//! - [`trie`](https://github.com/paritytech/trie)
//! - [`parity-common`](https://github.com/paritytech/parity-common)
//!
//! ## Trophy Section: Notable Downstream Projects
//!
//! A list of projects and tools in the blockchain ecosystem that one way or another use parts of
//! the Pezkuwi SDK:
//!
//! * [Avail](https://github.com/availproject/avail)
//! * [Cardano Partner Chains](https://iohk.io/en/blog/posts/2023/11/03/partner-chains-are-coming-to-cardano/)
//! * [Starknet's Madara Sequencer](https://github.com/keep-starknet-strange/madara)
//! * [Polymesh](https://polymesh.network/)
//!
//! [`substrate`]: crate::pezkuwi_sdk::substrate
//! [`frame`]: crate::pezkuwi_sdk::frame_runtime
//! [`cumulus`]: crate::pezkuwi_sdk::cumulus
//! [`pezkuwi`]: crate::pezkuwi_sdk::pezkuwi
//! [`xcm`]: crate::pezkuwi_sdk::xcm
//! [`frame-omni-bencher`]: https://crates.io/crates/frame-omni-bencher
//! [`pezkuwi-teyrchain-bin`]: https://crates.io/crates/polkadot-parachain-bin
//! [`pezkuwi-omni-node`]: https://crates.io/crates/polkadot-omni-node
/// Learn about Cumulus, the framework that transforms [`substrate`]-based chains into
/// [`pezkuwi`]-enabled teyrchains.
pub mod cumulus;
/// Learn about FRAME, the framework used to build Substrate runtimes.
pub mod frame_runtime;
/// Learn about Pezkuwi as a platform.
pub mod pezkuwi;
/// Learn about different ways through which smart contracts can be utilized on top of Substrate,
/// and in the Pezkuwi ecosystem.
pub mod smart_contracts;
/// Learn about Substrate, the main blockchain framework used in the Pezkuwi ecosystem.
pub mod substrate;
/// Index of all the templates that can act as first scaffold for a new project.
pub mod templates;
/// Learn about XCM, the de-facto communication language between different consensus systems.
pub mod xcm;
@@ -0,0 +1,96 @@
//! # Pezkuwi
//!
//! Implementation of the Pezkuwi node/host in Rust.
//!
//! ## Learn More and Get Involved
//!
//! - [Pezkuwi Forum](https://forum.polkadot.network/)
//! - [Pezkuwi Teyrchains](https://parachains.info/)
//! - [Pezkuwi (multi-chain) Explorer: Subscan](https://subscan.io/)
//! - Pezkuwi Fellowship
//! - [Manifesto](https://github.com/polkadot-fellows/manifesto/blob/main/manifesto.pdf)
//! - [Runtimes](https://github.com/polkadot-fellows/runtimes)
//! - [RFCs](https://github.com/polkadot-fellows/rfcs)
//! - [Dashboard](https://polkadot-fellows.github.io/dashboard/)
//! - [Pezkuwi Specs](http://spec.polkadot.network)
//! - [The Pezkuwi Teyrchain Host Implementers' Guide](https://docs.pezkuwichain.io/sdk/book/)
//! - [Pezkuwi papers](https://pezkuwichain.io/papers/)
//! - [JAM Graypaper](https://graypaper.com)
//!
//! ## Alternative Node Implementations 🌈
//!
//! - [Smoldot](https://docs.rs/crate/smoldot-light/latest). Pezkuwi light node/client.
//! - [KAGOME](https://github.com/qdrvm/kagome). C++ implementation of the Pezkuwi host.
//! - [Gossamer](https://github.com/ChainSafe/gossamer). Golang implementation of the Pezkuwi host.
//!
//! ## Platform
//!
//! In this section, we examine what platform Pezkuwi exactly provides to developers.
//!
//! ### Pezkuwi White Paper
//!
//! The original vision of Pezkuwi (everything in the whitepaper, which was eventually called
//! **Pezkuwi 1.0**) revolves around the following arguments:
//!
//! * Future is multi-chain, because we need different chains with different specialization to
//! achieve widespread goals.
//! * In other words, no single chain is good enough to achieve all goals.
//! * A multi-chain future will inadvertently suffer from fragmentation of economic security.
//! * This stake fragmentation will make communication over consensus system with varying security
//! levels inherently unsafe.
//!
//! Pezkuwi's answer to the above is:
//!
//! > The chains of the future must have a way to share their economic security, whilst maintaining
//! > their execution and governance sovereignty. These chains are called "Teyrchains".
//!
//! * Shared Security: The idea of shared economic security sits at the core of Pezkuwi. Pezkuwi
//! enables different teyrchains to pool their economic security from Pezkuwi (i.e. "*Relay
//! Chain*").
//! * (heterogenous) Sharded Execution: Yet, each teyrchain is free to have its own execution logic
//! (runtime), which also encompasses governance and sovereignty. Moreover, Pezkuwi ensures the
//! correct execution of all teyrchains, without having all of its validators re-execute all
//! teyrchain blocks. When seen from this perspective, Pezkuwi achieves the ability to verify
//! the validity of the block execution of multiple teyrchains using the same set of validators as
//! the Relay Chain. In practice, this means that the shards (teyrchains) share the same economic
//! security as the Relay Chain.
//! Learn about this process called [Approval Checking](https://pezkuwichain.io/blog/polkadot-v1-0-sharding-and-economic-security#approval-checking-and-finality).
//! * A framework to build blockchains: In order to materialize the ecosystem of teyrchains, an easy
//! blockchain framework must exist. This is [Substrate](crate::pezkuwi_sdk::substrate),
//! [FRAME](crate::pezkuwi_sdk::frame_runtime) and [Cumulus](crate::pezkuwi_sdk::cumulus).
//! * A communication language between blockchains: In order for these blockchains to communicate,
//! they need a shared language. [XCM](crate::pezkuwi_sdk::xcm) is one such language, and the one
//! that is most endorsed in the Pezkuwi ecosystem.
//!
//! > Note that the interoperability promised by Pezkuwi is unparalleled in that any two teyrchains
//! > connected to Pezkuwi have the same security and can have much better guarantees about the
//! > security of the recipient of any message.
//! > Bridges enable transaction and information flow between different consensus systems, crucial
//! > for Pezkuwi's multi-chain architecture. However, they can become the network's most
//! > vulnerable points. If a bridge's security measures are weaker than those of the connected
//! > blockchains, it poses a significant risk. Attackers might exploit these weaknesses to launch
//! > attacks such as theft or disruption of services.
//!
//! Pezkuwi delivers the above vision, alongside a flexible means for teyrchains to schedule
//! themselves with the Relay Chain. To achieve this, Pezkuwi has been developed with an
//! architecture similar to that of a computer. Pezkuwi Relay Chain has a number of "cores". Each
//! core is (in simple terms) capable of progressing 1 teyrchain at a time. For example, a teyrchain
//! can schedule itself on a single core for 5 relay chain blocks.
//!
//! Within the scope of Pezkuwi 1.x, two main scheduling ways have been considered:
//!
//! * Long term Teyrchains, obtained through locking a sum of HEZ in an auction system.
//! * On-demand Teyrchains, purchased through paying HEZ to the relay-chain whenever needed.
//!
//! ### The Future
//!
//! After delivering Pezkuwi 1.x, the future of Pezkuwi as a protocol and platform is in the hands
//! of the community and the fellowship. This is happening most notable through the RFC process.
//! Some of the RFCs that do alter Pezkuwi as a platform and have already passed are as follows:
//!
//! - RFC#1: [Agile-coretime](https://github.com/polkadot-fellows/RFCs/blob/main/text/0001-agile-coretime.md):
//! Agile periodic-sale-based model for assigning Coretime on the Pezkuwi Ubiquitous Computer.
//! - RFC#5: [Coretime-interface](https://github.com/polkadot-fellows/RFCs/blob/main/text/0005-coretime-interface.md):
//! Interface for manipulating the usage of cores on the Pezkuwi Ubiquitous Computer.
//!
//! Learn more about [Pezkuwi as a Computational Resource](https://wiki.network.pezkuwichain.io/docs/polkadot-direction#polkadot-as-a-computational-resource).
@@ -0,0 +1,9 @@
//! # Smart Contracts
//!
//! TODO: @cmichi <https://github.com/pezkuwichain/pezkuwi-sdk/issues/161>
//!
//! - WASM and EVM based, pallet-contracts and pallet-evm.
//! - single-daap-chain, transition from ink! to FRAME.
//! - Link to `use.ink`
//! - Link to [`crate::reference_docs::runtime_vs_smart_contract`].
//! - <https://use.ink/migrate-ink-contracts-to-polkadot-frame-parachain/>
@@ -0,0 +1,137 @@
//! # Substrate
//!
//! Substrate is a Rust framework for building blockchains in a modular and extensible way. While in
//! itself un-opinionated, it is the main engine behind the Pezkuwi ecosystem.
//!
//! ## Overview, Philosophy
//!
//! Substrate approaches blockchain development with an acknowledgement of a few self-evident
//! truths:
//!
//! 1. Society and technology evolves.
//! 2. Humans are fallible.
//!
//! This makes the task of designing a correct, safe and long-lasting blockchain system hard.
//!
//! Nonetheless, in strive towards achieving this goal, Substrate embraces the following:
//!
//! 1. Use of **Rust** as a modern and safe programming language, which limits human error through
//! various means, most notably memory and type safety.
//! 2. Substrate is written from the ground-up with a *generic, modular and extensible* design. This
//! ensures that software components can be easily swapped and upgraded. Examples of this is
//! multiple consensus mechanisms provided by Substrate, as listed below.
//! 3. Lastly, the final blockchain system created with the above properties needs to be
//! upgradeable. In order to achieve this, Substrate is designed as a meta-protocol, whereby the
//! application logic of the blockchain (called "Runtime") is encoded as a WASM blob, and is
//! stored in the state. The rest of the system (called "node") acts as the executor of the WASM
//! blob.
//!
//! In essence, the meta-protocol of all Substrate based chains is the "Runtime as WASM blob"
//! accord. This enables the Runtime to become inherently upgradeable, crucially without [forks](https://en.wikipedia.org/wiki/Fork_(blockchain)). The
//! upgrade is merely a matter of the WASM blob being changed in the state, which is, in principle,
//! same as updating an account's balance. Learn more about this in detail in
//! [`crate::reference_docs::wasm_meta_protocol`].
//!
//! > A great analogy for substrate is the following: Substrate node is a gaming console, and a WASM
//! > runtime, possibly created with FRAME is the game being inserted into the console.
//!
//! [`frame`], Substrate's default runtime development library, takes the above safety practices
//! even further by embracing a declarative programming model whereby correctness is enhanced and
//! the system is highly configurable through parameterization. Learn more about this in
//! [`crate::reference_docs::trait_based_programming`].
//!
//! ## How to Get Started
//!
//! Substrate offers different options at the spectrum of technical freedom <-> development ease.
//!
//! * The easiest way to use Substrate is to use one of the templates (some of which listed at
//! [`crate::pezkuwi_sdk::templates`]) and only tweak the parameters of the runtime or node. This
//! allows you to launch a blockchain in minutes, but is limited in technical freedom.
//! * Next, most developers wish to develop their custom runtime modules, for which the de-facto way
//! is [`frame`](crate::pezkuwi_sdk::frame_runtime).
//! * Finally, Substrate is highly configurable at the node side as well, but this is the most
//! technically demanding.
//!
//! > A notable Substrate-based blockchain that has built both custom FRAME pallets and custom
//! > node-side components is <https://github.com/Cardinal-Cryptography/aleph-node>.
#![doc = simple_mermaid::mermaid!("../../../mermaid/substrate_dev.mmd")]
//!
//! ## Structure
//!
//! Substrate contains a large number of crates, therefore it is useful to have an overview of what
//! they are, and how they are organized. In broad terms, these crates are divided into three
//! categories:
//!
//! * `sc-*` (short for *Substrate-client*) crates, located under `./client` folder. These are all
//! the crates that lead to the node software. Notable examples are [`sc_network`], various
//! consensus crates, RPC ([`sc_rpc_api`]) and database ([`sc_client_db`]), all of which are
//! expected to reside in the node side.
//! * `sp-*` (short for *substrate-primitives*) crates, located under `./primitives` folder. These
//! are crates that facilitate both the node and the runtime, but are not opinionated about what
//! framework is using for building the runtime. Notable examples are [`sp_api`] and [`sp_io`],
//! which form the communication bridge between the node and runtime.
//! * `pallet-*` and `frame-*` crates, located under `./frame` folder. These are the crates related
//! to FRAME. See [`frame`] for more information.
//!
//! ### WASM Build
//!
//! Many of the Substrate crates, such as entire `sp-*`, need to compile to both WASM (when a WASM
//! runtime is being generated) and native (for example, when testing). To achieve this, Substrate
//! follows the convention of the Rust community, and uses a `feature = "std"` to signify that a
//! crate is being built with the standard library, and is built for native. Otherwise, it is built
//! for `no_std`.
//!
//! This can be summarized in `#![cfg_attr(not(feature = "std"), no_std)]`, which you can often find
//! in any Substrate-based runtime.
//!
//! Substrate-based runtimes use [`substrate_wasm_builder`] in their `build.rs` to automatically
//! build their WASM files as a part of normal build command (e.g. `cargo build`). Once built, the
//! wasm file is placed in `./target/{debug|release}/wbuild/{runtime_name}/{runtime_name}.wasm`.
//!
//! In order to ensure that the WASM build is **deterministic**, the [Substrate Runtime Toolbox (srtool)](https://github.com/paritytech/srtool) can be used.
//!
//! ### Anatomy of a Binary Crate
//!
//! From the above, [`node_cli`]/[`kitchensink_runtime`] and `node-template` are essentially
//! blueprints of a Substrate-based project, as the name of the latter is implying. Each
//! Substrate-based project typically contains the following:
//!
//! * Under `./runtime`, a `./runtime/src/lib.rs` which is the top level runtime amalgamator file.
//! This file typically contains the [`frame::runtime::prelude::construct_runtime`] and
//! [`frame::runtime::prelude::impl_runtime_apis`] macro calls, which is the final definition of a
//! runtime.
//!
//! * Under `./node`, a `main.rs`, which is the starting point, and a `./service.rs`, which contains
//! all the node side components. Skimming this file yields an overview of the networking,
//! database, consensus and similar node side components.
//!
//! > The above two are conventions, not rules.
//!
//! > See <https://github.com/pezkuwichain/pezkuwi-sdk/issues/94> for an update on how the node side
//! > components are being amalgamated.
//!
//! ## Teyrchain?
//!
//! As noted above, Substrate is the main engine behind the Pezkuwi ecosystem. One of the ways
//! through which Pezkuwi can be utilized is by building "teyrchains", blockchains that are
//! connected to Pezkuwi's shared security.
//!
//! To build a teyrchain, one could use [Cumulus](crate::pezkuwi_sdk::cumulus), the library on
//! top of Substrate, empowering any substrate-based chain to be a Pezkuwi teyrchain.
//!
//! ## Where To Go Next?
//!
//! Additional noteworthy crates within substrate:
//!
//! - RPC APIs of a Substrate node: [`sc_rpc_api`]/[`sc_rpc`]
//! - CLI Options of a Substrate node: [`sc_cli`]
//! - All of the consensus related crates provided by Substrate:
//! - [`sc_consensus_aura`]
//! - [`sc_consensus_babe`]
//! - [`sc_consensus_grandpa`]
//! - [`sc_consensus_beefy`] (TODO: @adrian, add some high level docs <https://github.com/pezkuwichain/pezkuwi-sdk/issues/162>)
//! - [`sc_consensus_manual_seal`]
//! - [`sc_consensus_pow`]
#[doc(hidden)]
pub use crate::pezkuwi_sdk;
@@ -0,0 +1,44 @@
//! # Templates
//!
//! This document enumerates a non-exhaustive list of templates that one can use to get started with
//! pezkuwi-sdk.
//!
//! > Know more tools/templates that are not listed here? please contribute them by opening a PR.
//!
//! ## Internal
//!
//! The following templates are maintained as a part of the `pezkuwi-sdk` repository:
//!
//! - [`minimal-template`](https://github.com/pezkuwichain/pezkuwi-sdk/issues/25): A minimal
//! template that contains the least amount of features to be a functioning blockchain. Suitable
//! for learning and testing.
//! - [`solochain-template`](https://github.com/pezkuwichain/pezkuwi-sdk/issues/25): Formerly known
//! as "substrate-node-template", is a white-labeled substrate-based blockchain (aka. solochain)
//! that contains moderate features, such as a basic consensus engine and some FRAME pallets. This
//! template can act as a good starting point for those who want to launch a solochain.
//! - [`teyrchain-template`](https://github.com/pezkuwichain/pezkuwi-sdk-teyrchain-template):
//! A teyrchain template ready to be connected to a relay-chain, such as [Paseo](https://github.com/paseo-network/.github)
//! , Kusama or Pezkuwi.
//!
//! Note that these templates are mirrored automatically from [this](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/templates)
//! directory of pezkuwi-sdk, therefore any changes to them should be made as a PR to this repo.
//!
//! ## OpenZeppelin
//!
//! In June 2023, OpenZeppelin was awarded a grant from the Pezkuwi
//! treasury for building a number of Pezkuwi-sdk
//! based templates. These templates are a great starting point for developers and newcomers.
//! So far OpenZeppelin has released two templates, which have been fully [audited](https://github.com/pezkuwichain/polkadot-runtime-templates/tree/main/audits):
//! - [`generic-runtime-template`](https://github.com/pezkuwichain/polkadot-runtime-templates?tab=readme-ov-file#generic-runtime-template):
//! A minimal template that has all the common pallets that teyrchains use with secure defaults.
//! - [`evm-runtime-template`](https://github.com/pezkuwichain/polkadot-runtime-templates/tree/main?tab=readme-ov-file#evm-template):
//! This template has EVM compatibility out of the box and allows migrating your solidity contracts
//! or EVM compatible dapps easily. It also uses 20 byte addresses like Ethereum and has some
//! Account Abstraction support.
//!
//! ## POP-CLI
//!
//! Is a CLI tool capable of scaffolding a new pezkuwi-sdk-based project, possibly removing the
//! need for templates.
//!
//! - <https://pop.r0gue.io/cli/>
@@ -0,0 +1,70 @@
//! # XCM
//!
//! XCM, or Cross-Consensus Messaging, is a **language** to communicate **intentions** between
//! **consensus systems**.
//!
//! ## Overview
//!
//! XCM is a standard, specification of which lives in the [xcm format repo](https://github.com/paritytech/xcm-format).
//! It's agnostic both in programming language and blockchain platform, which means it could be used
//! in Rust in Pezkuwi, or in Go or C++ in any other platform like Cosmos or Ethereum.
//!
//! It enables different consensus systems to communicate with each other in an expressive manner.
//! Consensus systems include blockchains, smart contracts, and any other state machine that
//! achieves consensus in some way.
//!
//! XCM is executed on a virtual machine called the XCVM.
//! Scripts can be written with the XCM language, which are often called XCMs, messages or XCM
//! programs. Each program is a series of instructions, which get executed one after the other by
//! the virtual machine. These instructions aim to encompass all major things users typically do in
//! consensus systems. There are instructions on asset transferring, teleporting, locking, among
//! others. New instructions are added and changes to the XCVM are made via the [RFC process](https://github.com/paritytech/xcm-format/blob/master/proposals/0032-process.md).
//!
//! ## In Pezkuwi SDK
//!
//! The Pezkuwi SDK allows for easily deploying sovereign blockchains from scratch, all very
//! customizable. Dealing with many heterogeneous blockchains can be cumbersome.
//! XCM allows all these blockchains to communicate with an agreed-upon language.
//! As long as an implementation of the XCVM is implemented, the same XCM program can be executed in
//! all blockchains and perform the same task.
//!
//! ## Implementation
//!
//! A ready-to-use Rust implementation lives in the [pezkuwi-sdk repo](https://github.com/paritytech/polkadot-sdk/tree/master/polkadot/xcm),
//! but will be moved to its own repo in the future.
//!
//! Its main components are:
//! - [`xcm`](::xcm): The definition of the basic types and instructions.
//! - [`xcm_executor`]: An implementation of the virtual machine to execute instructions.
//! - [`pallet_xcm`]: A FRAME pallet for interacting with the executor.
//! - [`xcm_builder`]: A collection of types to configure the executor.
//! - [`xcm_simulator`]: A playground for trying out different XCM programs and executor
//! configurations.
//!
//! ## Example
//!
//! To perform the very usual operation of transferring assets, the following XCM program can be
//! used:
#![doc = docify::embed!("src/pezkuwi_sdk/xcm.rs", example_transfer)]
//!
//! ## Get started
//!
//! To learn how it works and to get started, go to the [XCM docs](xcm_docs).
#[cfg(test)]
mod tests {
use xcm::latest::prelude::*;
#[docify::export]
#[test]
fn example_transfer() {
let _transfer_program = Xcm::<()>(vec![
WithdrawAsset((Here, 100u128).into()),
BuyExecution { fees: (Here, 100u128).into(), weight_limit: Unlimited },
DepositAsset {
assets: All.into(),
beneficiary: AccountId32 { id: [0u8; 32].into(), network: None }.into(),
},
]);
}
}
@@ -0,0 +1,29 @@
//! # State Transition Function
//!
//! This document briefly explains how in the context of Substrate-based blockchains, we view the
//! blockchain as a **decentralized state transition function**.
//!
//! Recall that a blockchain's main purpose is to help a permissionless set of entities to agree on
//! a shared data-set, and how it evolves. This is called the **State**, also referred to as
//! "onchain" data, or *Storage* in the context of FRAME. The state is where the account balance of
//! each user is, for example, stored, and there is a canonical version of it that everyone agrees
//! upon.
//!
//! Then, recall that a typical blockchain system will alter its state through execution of blocks.
//! *The component that dictates how this state alteration can happen is called the state transition
//! function*.
#![doc = simple_mermaid::mermaid!("../../../mermaid/stf_simple.mmd")]
//!
//! In Substrate-based blockchains, the state transition function is called the *Runtime*. This is
//! explained further in [`crate::reference_docs::wasm_meta_protocol`].
//!
//! With this in mind, we can paint a complete picture of a blockchain as a state machine:
#![doc = simple_mermaid::mermaid!("../../../mermaid/stf.mmd")]
//!
//! In essence, the state of the blockchain at block N is the outcome of applying the state
//! transition function to the previous state, and the current block as input. This can be
//! mathematically represented as:
//!
//! ```math
//! STF = F(State_N, Block_N) -> State_{N+1}
//! ```
@@ -0,0 +1,200 @@
//! # What is a chain specification
//!
//! A chain specification file defines the set of properties that are required to run the node as
//! part of the chain. The chain specification consists of two main parts:
//! - initial state of the runtime,
//! - network / logical properties of the chain, the most important property being the list of
//! bootnodes.
//!
//! This document describes how the initial state is handled in pallets and runtime, and how to
//! interact with the runtime in order to build the genesis state.
//!
//! For more information on chain specification and its properties, refer to
//! [`sc_chain_spec#from-initial-state-to-raw-genesis`].
//!
//! The initial genesis state can be provided in the following formats:
//! - full
//! - patch
//! - raw
//!
//! Each of the formats is explained in [_chain-spec-format_][`sc_chain_spec#chain-spec-formats`].
//!
//!
//! # `GenesisConfig` for `pallet`
//!
//! Every frame pallet may have its initial state which is defined by the `GenesisConfig` internal
//! struct. It is a regular Rust struct, annotated with the [`pallet::genesis_config`] attribute.
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_GenesisConfig)]
//!
//! The struct shall be defined within the pallet `mod`, as in the following code:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar)]
//!
//! The initial state conveyed in the `GenesisConfig` struct is transformed into state storage
//! items by means of the [`BuildGenesisConfig`] trait, which shall be implemented for the pallet's
//! `GenesisConfig` struct. The [`pallet::genesis_build`] attribute shall be attached to the `impl`
//! block:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_build)]
//!
//! `GenesisConfig` may also contain more complicated types, including nested structs or enums, as
//! in the example for `pallet_foo`:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_GenesisConfig)]
//!
//! Note that [`serde`] attributes can be used to control how the data
//! structures are stored into JSON. In the following example, the [`sp_core::bytes`] function is
//! used to serialize the `values` field.
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", SomeFooData2)]
//!
//! Please note that fields of `GenesisConfig` may not be directly mapped to storage items. In the
//! following example, the initial struct fields are used to compute (sum) the value that will be
//! stored in the state as `ProcessedEnumValue`:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_build)]
//!
//! # `GenesisConfig` for `runtimes`
//!
//! The runtime genesis config struct consists of configs for every pallet. For the [_demonstration
//! runtime_][`chain_spec_guide_runtime`] used in this guide, it consists of `SystemConfig`,
//! `BarConfig`, and `FooConfig`. This structure was automatically generated by a macro and it can
//! be sneak-peeked here: [`RuntimeGenesisConfig`]. For further reading on generated runtime
//! types, refer to [`frame_runtime_types`].
//!
//! The macro automatically adds an attribute that renames all the fields to [`camelCase`]. It is a
//! good practice to add it to nested structures too, to have the naming of the JSON keys consistent
//! across the chain-spec file.
//!
//! ## `Default` for `GenesisConfig`
//!
//! `GenesisConfig` of all pallets must implement the `Default` trait. These are aggregated into
//! the runtime's `RuntimeGenesisConfig`'s `Default`.
//!
//! The default value of `RuntimeGenesisConfig` can be queried by the [`GenesisBuilder::get_preset`]
//! function provided by the runtime with `id:None`.
//!
//! A default value for `RuntimeGenesisConfig` usually is not operational. This is because for some
//! pallets it is not possible to define good defaults (e.g. an initial set of authorities).
//!
//! A default value is a base upon which a patch for `GenesisConfig` is applied. A good description
//! of how it exactly works is provided in [`get_storage_for_patch`] (and also in
//! [`GenesisBuilder::get_preset`]). A patch can be provided as an external file (manually created)
//! or as a built-in runtime preset. More info on presets is in the material to follow.
//!
//! ## Implementing `GenesisBuilder` for runtime
//!
//! The runtime exposes a dedicated runtime API for interacting with its genesis config:
//! [`sp_genesis_builder::GenesisBuilder`]. The implementation shall be provided within
//! the [`sp_api::impl_runtime_apis`] macro, typically making use of some helpers provided:
//! [`build_state`], [`get_preset`].
//! A typical implementation of [`sp_genesis_builder::GenesisBuilder`] looks as follows:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/runtime.rs", runtime_impl)]
//!
//! Please note that two functions are customized: `preset_names` and `get_preset`. The first one
//! just provides a `Vec` of the names of supported presets, while the latter delegates the call
//! to a function that maps the name to an actual preset:
//! [`chain_spec_guide_runtime::presets::get_builtin_preset`]
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)]
//!
//! ## Genesis state presets for runtime
//!
//! The runtime may provide many flavors of initial genesis state. This may be useful for predefined
//! testing networks, local development, or CI integration tests. Predefined genesis state may
//! contain a list of pre-funded accounts, predefined authorities for consensus, sudo key, and many
//! others useful for testing.
//!
//! Internally, presets can be provided in a number of ways:
//! - using [`build_struct_json_patch`] macro (**recommended**):
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_2)]
//! - JSON using runtime types to serialize values:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)]
//! - JSON in string form:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)]
//!
//! It is worth noting that a preset does not have to be the full `RuntimeGenesisConfig`, in that
//! sense that it does not have to contain all the keys of the struct. The preset is actually a JSON
//! patch that will be merged with the default value of `RuntimeGenesisConfig`. This approach should
//! simplify maintenance of built-in presets. The following example illustrates a runtime genesis
//! config patch with a single key built using [`build_struct_json_patch`] macro:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_4)]
//! This results in the following JSON blob:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", preset_4_json)]
//!
//!
//! ## Note on the importance of testing presets
//!
//! It is recommended to always test presets by adding tests that convert the preset into the
//! raw storage. Converting to raw storage involves the deserialization of the provided JSON blob,
//! which enforces the verification of the preset. The following code shows one of the approaches
//! that can be taken for testing:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", check_presets)]
//!
//! ## Note on the importance of using the `deny_unknown_fields` attribute
//!
//! It is worth noting that when manually building preset JSON blobs it is easy to make a
//! hard-to-spot mistake, as in the following example ([`FooStruct`] does not contain `fieldC`):
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_invalid)]
//! Even though `preset_invalid` contains a key that does not exist, the deserialization of the JSON
//! blob does not fail. The misspelling is silently ignored due to the lack of the
//! [`deny_unknown_fields`] attribute on the [`FooStruct`] struct, which is internally used in
//! `GenesisConfig`.
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", invalid_preset_works)]
//!
//! To avoid this problem [`build_struct_json_patch`] macro shall be used whenever possible (it
//! internally instantiates the struct before serializang it JSON blob, so all unknown fields shall
//! be caught at compilation time).
//!
//! ## Runtime `GenesisConfig` raw format
//!
//! A raw format of genesis config contains just the state's keys and values as they are stored in
//! the storage. This format is used to directly initialize the genesis storage. This format is
//! useful for long-term running chains, where the `GenesisConfig` structure for pallets may be
//! evolving over time. The JSON representation created at some point in time may no longer be
//! deserializable in the future, making a chain specification useless. The raw format is
//! recommended for production chains.
//!
//! For a detailed description of how the raw format is built, please refer to
//! [_chain-spec-raw-genesis_][`sc_chain_spec#from-initial-state-to-raw-genesis`]. Plain and
//! corresponding raw examples of chain-spec are given in
//! [_chain-spec-examples_][`sc_chain_spec#json-chain-specification-example`].
//! The [`chain_spec_builder`] util supports building the raw storage.
//!
//! # Interacting with the tool
//!
//! The [`chain_spec_builder`] util allows interaction with the runtime in order to list or display
//! presets and build the chain specification file. It is possible to use the tool with the
//! [_demonstration runtime_][`chain_spec_guide_runtime`]. To build the required packages, just run
//! the following command:
//!
//! ```ignore
//! cargo build -p staging-chain-spec-builder -p chain-spec-guide-runtime --release
//! ```
//!
//! The `chain-spec-builder` util can also be installed with `cargo install`:
//!
//! ```ignore
//! cargo install staging-chain-spec-builder
//! cargo build -p chain-spec-guide-runtime --release
//! ```
//! Here are some examples in the form of rust tests:
//! ## Listing available preset names:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", cmd_list_presets)]
//! ## Displaying preset with given name
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", cmd_get_preset)]
//! ## Building a solo chain-spec (the default) using given preset
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", cmd_generate_chain_spec)]
//! ## Building a teyrchain chain-spec using given preset
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", cmd_generate_para_chain_spec)]
//!
//! [`RuntimeGenesisConfig`]:
//! chain_spec_guide_runtime::runtime::RuntimeGenesisConfig
//! [`FooStruct`]:
//! chain_spec_guide_runtime::pallets::FooStruct
//! [`impl_runtime_apis`]: frame::runtime::prelude::impl_runtime_apis
//! [`build_state`]: frame_support::genesis_builder_helper::build_state
//! [`get_preset`]: frame_support::genesis_builder_helper::get_preset
//! [`pallet::genesis_build`]: frame_support::pallet_macros::genesis_build
//! [`pallet::genesis_config`]: frame_support::pallet_macros::genesis_config
//! [`build_struct_json_patch`]: frame_support::build_struct_json_patch
//! [`BuildGenesisConfig`]: frame_support::traits::BuildGenesisConfig
//! [`serde`]: https://serde.rs/field-attrs.html
//! [`get_storage_for_patch`]: sc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch
//! [`GenesisBuilder::get_preset`]: sp_genesis_builder::GenesisBuilder::get_preset
//! [`deny_unknown_fields`]: https://serde.rs/container-attrs.html#deny_unknown_fields
//! [`camelCase`]: https://serde.rs/container-attrs.html#rename_all
@@ -0,0 +1,64 @@
[package]
name = "chain-spec-guide-runtime"
description = "A minimal runtime for chain spec guide"
version = "0.0.0"
license = "MIT-0"
authors.workspace = true
homepage.workspace = true
repository.workspace = true
edition.workspace = true
publish = false
[dependencies]
codec = { workspace = true }
docify = { workspace = true }
frame-support = { workspace = true }
scale-info = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
# this is a frame-based runtime, thus importing `frame` with runtime feature enabled.
frame = { features = ["experimental", "runtime"], workspace = true }
# genesis builder that allows us to interact with runtime genesis config
sp-application-crypto = { features = ["serde"], workspace = true }
sp-core = { workspace = true }
sp-genesis-builder = { workspace = true }
sp-keyring = { workspace = true }
sp-runtime = { features = ["serde"], workspace = true }
[dev-dependencies]
cmd_lib = { workspace = true }
sc-chain-spec = { workspace = true, default-features = true }
[build-dependencies]
substrate-wasm-builder = { optional = true, workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"frame-support/std",
"frame/std",
"sp-application-crypto/std",
"sp-core/std",
"sp-genesis-builder/std",
"sp-keyring/std",
"sp-runtime/std",
"serde/std",
"serde_json/std",
"substrate-wasm-builder",
]
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame/runtime-benchmarks",
"sc-chain-spec/runtime-benchmarks",
"sp-genesis-builder/runtime-benchmarks",
"sp-keyring/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"substrate-wasm-builder?/runtime-benchmarks",
]
@@ -0,0 +1,23 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
fn main() {
#[cfg(feature = "std")]
{
substrate_wasm_builder::WasmBuilder::build_using_defaults();
}
}
@@ -0,0 +1,29 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg_attr(not(feature = "std"), no_std)]
//! A minimal runtime that shows runtime genesis state.
extern crate alloc;
pub mod pallets;
pub mod presets;
pub mod runtime;
#[cfg(feature = "std")]
pub use runtime::{WASM_BINARY, WASM_BINARY_BLOATY};
@@ -0,0 +1,138 @@
// 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 for the chain-spec demo runtime.
use alloc::vec::Vec;
use frame::prelude::*;
#[docify::export]
#[frame::pallet(dev_mode)]
pub mod pallet_bar {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::storage]
pub(super) type InitialAccount<T: Config> = StorageValue<Value = T::AccountId>;
/// Simple `GenesisConfig`.
#[pallet::genesis_config]
#[derive(DefaultNoBound)]
#[docify::export(pallet_bar_GenesisConfig)]
pub struct GenesisConfig<T: Config> {
pub initial_account: Option<T::AccountId>,
}
#[pallet::genesis_build]
#[docify::export(pallet_bar_build)]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
/// The storage building function that presents a direct mapping of the initial config
/// values to the storage items.
fn build(&self) {
InitialAccount::<T>::set(self.initial_account.clone());
}
}
}
/// The sample structure used in `GenesisConfig`.
///
/// This structure does not deny unknown fields. This may lead to some problems.
#[derive(Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FooStruct {
pub field_a: u8,
pub field_b: u8,
}
/// The sample structure used in `GenesisConfig`.
///
/// This structure does not deny unknown fields. This may lead to some problems.
#[derive(Default, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct SomeFooData1 {
pub a: u8,
pub b: u8,
}
/// Another sample structure used in `GenesisConfig`.
///
/// The user defined serialization is used.
#[derive(Default, serde::Serialize, serde::Deserialize)]
#[docify::export]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct SomeFooData2 {
#[serde(default, with = "sp_core::bytes")]
pub values: Vec<u8>,
}
/// Sample enum used in `GenesisConfig`.
#[derive(Default, serde::Serialize, serde::Deserialize)]
pub enum FooEnum {
#[default]
Data0,
Data1(SomeFooData1),
Data2(SomeFooData2),
}
#[docify::export]
#[frame::pallet(dev_mode)]
pub mod pallet_foo {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::storage]
pub type ProcessedEnumValue<T> = StorageValue<Value = u64>;
#[pallet::storage]
pub type SomeInteger<T> = StorageValue<Value = u32>;
/// The more sophisticated structure for conveying initial state.
#[docify::export(pallet_foo_GenesisConfig)]
#[pallet::genesis_config]
#[derive(DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
pub some_integer: u32,
pub some_enum: FooEnum,
pub some_struct: FooStruct,
#[serde(skip)]
pub _phantom: PhantomData<T>,
}
#[pallet::genesis_build]
#[docify::export(pallet_foo_build)]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
/// The build method that indirectly maps an initial config values into the storage items.
fn build(&self) {
let processed_value: u64 = match &self.some_enum {
FooEnum::Data0 => 0,
FooEnum::Data1(v) => (v.a + v.b).into(),
FooEnum::Data2(v) => v.values.iter().map(|v| *v as u64).sum(),
};
ProcessedEnumValue::<T>::set(Some(processed_value));
SomeInteger::<T>::set(Some(self.some_integer));
}
}
}
@@ -0,0 +1,164 @@
// 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.
//! Presets for the chain-spec demo runtime.
use crate::{
pallets::{FooEnum, SomeFooData1, SomeFooData2},
runtime::{BarConfig, FooConfig, RuntimeGenesisConfig},
};
use alloc::vec;
use frame_support::build_struct_json_patch;
use serde_json::{json, to_string, Value};
use sp_application_crypto::Ss58Codec;
use sp_keyring::Sr25519Keyring;
/// A demo preset with strings only.
pub const PRESET_1: &str = "preset_1";
/// A demo preset with real types.
pub const PRESET_2: &str = "preset_2";
/// Another demo preset with real types and manually created json object.
pub const PRESET_3: &str = "preset_3";
/// A single value patch preset.
pub const PRESET_4: &str = "preset_4";
/// A single value patch preset.
pub const PRESET_INVALID: &str = "preset_invalid";
#[docify::export]
/// Function provides a preset demonstrating how use string representation of preset's internal
/// values.
fn preset_1() -> Value {
json!({
"bar": {
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
},
"foo": {
"someEnum": {
"Data2": {
"values": "0x0c0f"
}
},
"someStruct" : {
"fieldA": 10,
"fieldB": 20
},
"someInteger": 100
},
})
}
#[docify::export]
/// Function provides a preset demonstrating how to create a preset using
/// [`build_struct_json_patch`] macro.
fn preset_2() -> Value {
build_struct_json_patch!(RuntimeGenesisConfig {
foo: FooConfig {
some_integer: 200,
some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] })
},
bar: BarConfig { initial_account: Some(Sr25519Keyring::Ferdie.public().into()) },
})
}
#[docify::export]
/// Function provides a preset demonstrating how use the actual types to manually create a JSON
/// representing the preset.
fn preset_3() -> Value {
json!({
"bar": {
"initialAccount": Sr25519Keyring::Alice.public().to_ss58check(),
},
"foo": {
"someEnum": FooEnum::Data1(
SomeFooData1 {
a: 12,
b: 16
}
),
"someInteger": 300
},
})
}
#[docify::export]
/// Function provides a minimal preset demonstrating how to patch single key in
/// `RuntimeGenesisConfig` using [`build_struct_json_patch`] macro.
pub fn preset_4() -> Value {
build_struct_json_patch!(RuntimeGenesisConfig {
foo: FooConfig { some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] }) },
})
}
#[docify::export]
/// Function provides an invalid preset demonstrating how important is use of
/// `deny_unknown_fields` in data structures used in `GenesisConfig`.
fn preset_invalid() -> Value {
json!({
"foo": {
"someStruct": {
"fieldC": 5
},
},
})
}
/// Provides a JSON representation of preset identified by given `id`.
///
/// If no preset with given `id` exits `None` is returned.
#[docify::export]
pub fn get_builtin_preset(id: &sp_genesis_builder::PresetId) -> Option<alloc::vec::Vec<u8>> {
let preset = match id.as_ref() {
PRESET_1 => preset_1(),
PRESET_2 => preset_2(),
PRESET_3 => preset_3(),
PRESET_4 => preset_4(),
PRESET_INVALID => preset_invalid(),
_ => return None,
};
Some(
to_string(&preset)
.expect("serialization to json is expected to work. qed.")
.into_bytes(),
)
}
#[test]
#[docify::export]
fn check_presets() {
let builder = sc_chain_spec::GenesisConfigBuilderRuntimeCaller::<()>::new(
crate::WASM_BINARY.expect("wasm binary shall exists"),
);
assert!(builder.get_storage_for_named_preset(Some(&PRESET_1.to_string())).is_ok());
assert!(builder.get_storage_for_named_preset(Some(&PRESET_2.to_string())).is_ok());
assert!(builder.get_storage_for_named_preset(Some(&PRESET_3.to_string())).is_ok());
assert!(builder.get_storage_for_named_preset(Some(&PRESET_4.to_string())).is_ok());
}
#[test]
#[docify::export]
fn invalid_preset_works() {
let builder = sc_chain_spec::GenesisConfigBuilderRuntimeCaller::<()>::new(
crate::WASM_BINARY.expect("wasm binary shall exists"),
);
// Even though a preset contains invalid_key, conversion to raw storage does not fail. This is
// because the [`FooStruct`] structure is not annotated with `deny_unknown_fields` [`serde`]
// attribute.
// This may lead to hard to debug problems, that's why using ['deny_unknown_fields'] is
// recommended.
assert!(builder.get_storage_for_named_preset(Some(&PRESET_INVALID.to_string())).is_ok());
}
@@ -0,0 +1,127 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! A minimal runtime that shows runtime genesis state.
// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use crate::{
pallets::{pallet_bar, pallet_foo},
presets::*,
};
use alloc::{vec, vec::Vec};
use frame::{
deps::frame_support::{
genesis_builder_helper::{build_state, get_preset},
runtime,
},
prelude::*,
runtime::{apis, prelude::*},
};
use sp_genesis_builder::PresetId;
/// The runtime version.
#[runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: alloc::borrow::Cow::Borrowed("minimal-template-runtime"),
impl_name: alloc::borrow::Cow::Borrowed("minimal-template-runtime"),
authoring_version: 1,
spec_version: 0,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
system_version: 1,
};
/// The signed extensions that are added to the runtime.
type SignedExtra = ();
// Composes the runtime by adding all the used pallets and deriving necessary types.
#[runtime]
mod runtime {
/// The main runtime type.
#[runtime::runtime]
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeTask,
RuntimeViewFunction
)]
pub struct Runtime;
/// Mandatory system pallet that should always be included in a FRAME runtime.
#[runtime::pallet_index(0)]
pub type System = frame_system;
/// Sample pallet 1
#[runtime::pallet_index(1)]
pub type Bar = pallet_bar;
/// Sample pallet 2
#[runtime::pallet_index(2)]
pub type Foo = pallet_foo;
}
parameter_types! {
pub const Version: RuntimeVersion = VERSION;
}
/// Implements the types required for the system pallet.
#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = Block;
type Version = Version;
}
impl pallet_bar::Config for Runtime {}
impl pallet_foo::Config for Runtime {}
type Block = frame::runtime::types_common::BlockOf<Runtime, SignedExtra>;
type Header = HeaderFor<Runtime>;
#[docify::export(runtime_impl)]
impl_runtime_apis! {
impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
build_state::<RuntimeGenesisConfig>(config)
}
fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
get_preset::<RuntimeGenesisConfig>(id, get_builtin_preset)
}
fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
vec![
PresetId::from(PRESET_1),
PresetId::from(PRESET_2),
PresetId::from(PRESET_3),
PresetId::from(PRESET_4),
PresetId::from(PRESET_INVALID)
]
}
}
impl apis::Core<Block> for Runtime {
fn version() -> RuntimeVersion { VERSION }
fn execute_block(_: <Block as frame::traits::Block>::LazyBlock) { }
fn initialize_block(_: &Header) -> ExtrinsicInclusionMode { ExtrinsicInclusionMode::default() }
}
}
@@ -0,0 +1,203 @@
use cmd_lib::*;
use serde_json::{json, Value};
use std::str;
fn wasm_file_path() -> &'static str {
chain_spec_guide_runtime::runtime::WASM_BINARY_PATH
.expect("chain_spec_guide_runtime wasm should exist. qed")
}
const CHAIN_SPEC_BUILDER_PATH: &str = "../../../../../target/release/chain-spec-builder";
macro_rules! bash(
( chain-spec-builder $($a:tt)* ) => {{
let path = get_chain_spec_builder_path();
spawn_with_output!(
$path $($a)*
)
.expect("a process running. qed")
.wait_with_output()
.expect("to get output. qed.")
}}
);
fn get_chain_spec_builder_path() -> &'static str {
run_cmd!(
cargo build --release -p staging-chain-spec-builder --bin chain-spec-builder
)
.expect("Failed to execute command");
CHAIN_SPEC_BUILDER_PATH
}
#[docify::export_content]
fn cmd_list_presets(runtime_path: &str) -> String {
bash!(
chain-spec-builder list-presets -r $runtime_path
)
}
#[test]
fn list_presets() {
let output: serde_json::Value =
serde_json::from_slice(cmd_list_presets(wasm_file_path()).as_bytes()).unwrap();
assert_eq!(
output,
json!({
"presets":[
"preset_1",
"preset_2",
"preset_3",
"preset_4",
"preset_invalid"
]
}),
"Output did not match expected"
);
}
#[docify::export_content]
fn cmd_get_preset(runtime_path: &str) -> String {
bash!(
chain-spec-builder display-preset -r $runtime_path -p preset_2
)
}
#[test]
fn get_preset() {
let output: serde_json::Value =
serde_json::from_slice(cmd_get_preset(wasm_file_path()).as_bytes()).unwrap();
assert_eq!(
output,
json!({
"bar": {
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
},
"foo": {
"someEnum": {
"Data2": {
"values": "0x0c10"
}
},
"someInteger": 200
},
}),
"Output did not match expected"
);
}
#[docify::export_content]
fn cmd_generate_chain_spec(runtime_path: &str) -> String {
bash!(
chain-spec-builder -c /dev/stdout create -r $runtime_path named-preset preset_2
)
}
#[test]
fn generate_chain_spec() {
let mut output: serde_json::Value =
serde_json::from_slice(cmd_generate_chain_spec(wasm_file_path()).as_bytes()).unwrap();
if let Some(code) = output["genesis"]["runtimeGenesis"].as_object_mut().unwrap().get_mut("code")
{
*code = Value::String("0x123".to_string());
}
assert_eq!(
output,
json!({
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" },
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x123",
"patch": {
"bar": {
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"
},
"foo": {
"someEnum": {
"Data2": {
"values": "0x0c10"
}
},
"someInteger": 200
}
}
}
}
}),
"Output did not match expected"
);
}
#[docify::export_content]
fn cmd_generate_para_chain_spec(runtime_path: &str) -> String {
bash!(
chain-spec-builder -c /dev/stdout create -c pezkuwi -p 1000 -r $runtime_path named-preset preset_2
)
}
#[test]
fn generate_para_chain_spec() {
let mut output: serde_json::Value =
serde_json::from_slice(cmd_generate_para_chain_spec(wasm_file_path()).as_bytes()).unwrap();
if let Some(code) = output["genesis"]["runtimeGenesis"].as_object_mut().unwrap().get_mut("code")
{
*code = Value::String("0x123".to_string());
}
assert_eq!(
output,
json!({
"name": "Custom",
"id": "custom",
"chainType": "Live",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"relay_chain": "pezkuwi",
"para_id": 1000,
"properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" },
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x123",
"patch": {
"bar": {
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"
},
"foo": {
"someEnum": {
"Data2": {
"values": "0x0c10"
}
},
"someInteger": 200
}
}
}
}}),
"Output did not match expected"
);
}
#[test]
#[docify::export_content]
fn preset_4_json() {
assert_eq!(
chain_spec_guide_runtime::presets::preset_4(),
json!({
"foo": {
"someEnum": {
"Data2": {
"values": "0x0c10"
}
},
},
})
);
}
@@ -0,0 +1,104 @@
//! # Substrate CLI
//!
//! Let's see some examples of typical CLI arguments used when setting up and running a
//! Substrate-based blockchain. We use the [`solochain-template`](https://github.com/pezkuwichain/pezkuwi-sdk/issues/25)
//! on these examples.
//!
//! #### Checking the available CLI arguments
//! ```bash
//! ./target/debug/node-template --help
//! ```
//! - `--help`: Displays the available CLI arguments.
//!
//! #### Starting a Local Substrate Node in Development Mode
//! ```bash
//! ./target/release/node-template \
//! --dev
//! ```
//! - `--dev`: Runs the node in development mode, using a pre-defined development chain
//! specification.
//! This mode ensures a fresh state by deleting existing data on restart.
//!
//! #### Generating Custom Chain Specification
//! ```bash
//! ./target/debug/node-template \
//! build-spec \
//! --disable-default-bootnode \
//! --chain local \
//! > customSpec.json
//! ```
//!
//! - `build-spec`: A subcommand to generate a chain specification file.
//! - `--disable-default-bootnode`: Disables the default bootnodes in the node template.
//! - `--chain local`: Indicates the chain specification is for a local development chain.
//! - `> customSpec.json`: Redirects the output into a customSpec.json file.
//!
//! #### Converting Chain Specification to Raw Format
//! ```bash
//! ./target/debug/node-template build-spec \
//! --chain=customSpec.json \
//! --raw \
//! --disable-default-bootnode \
//! > customSpecRaw.json
//! ```
//!
//! - `--chain=customSpec.json`: Uses the custom chain specification as input.
//! - `--disable-default-bootnode`: Disables the default bootnodes in the node template.
//! - `--raw`: Converts the chain specification into a raw format with encoded storage keys.
//! - `> customSpecRaw.json`: Outputs to `customSpecRaw.json`.
//!
//! #### Starting the First Node in a Private Network
//! ```bash
//! ./target/debug/node-template \
//! --base-path /tmp/node01 \
//! --chain ./customSpecRaw.json \
//! --port 30333 \
//! --ws-port 9945 \
//! --rpc-port 9933 \
//! --telemetry-url "wss://telemetry.pezkuwichain.io/submit/ 0" \
//! --validator \
//! --rpc-methods Unsafe \
//! --name MyNode01
//! ```
//!
//! - `--base-path`: Sets the directory for node data.
//! - `--chain`: Specifies the chain specification file.
//! - `--port`: TCP port for peer-to-peer communication.
//! - `--ws-port`: WebSocket port for RPC.
//! - `--rpc-port`: HTTP port for JSON-RPC.
//! - `--telemetry-url`: Endpoint for sending telemetry data.
//! - `--validator`: Indicates the nodes participation in block production.
//! - `--rpc-methods Unsafe`: Allows potentially unsafe RPC methods.
//! - `--name`: Sets a human-readable name for the node.
//!
//! #### Adding a Second Node to the Network
//! ```bash
//! ./target/release/node-template \
//! --base-path /tmp/bob \
//! --chain local \
//! --bob \
//! --port 30334 \
//! --rpc-port 9946 \
//! --telemetry-url "wss://telemetry.pezkuwichain.io/submit/ 0" \
//! --validator \
//! --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp
//! ```
//!
//! - `--base-path`: Sets the directory for node data.
//! - `--chain`: Specifies the chain specification file.
//! - `--bob`: Initializes the node with the session keys of the "Bob" account.
//! - `--port`: TCP port for peer-to-peer communication.
//! - `--rpc-port`: HTTP port for JSON-RPC.
//! - `--telemetry-url`: Endpoint for sending telemetry data.
//! - `--validator`: Indicates the nodes participation in block production.
//! - `--bootnodes`: Specifies the address of the first node for peer discovery. Nodes should find
//! each other using mDNS. This command needs to be used if they don't find each other.
//!
//! ---
//!
//! > If you are interested in learning how to extend the CLI with your custom arguments, you can
//! > check out the [Customize your Substrate chain CLI](https://www.youtube.com/watch?v=IVifko1fqjw)
//! > seminar.
//! > Please note that the seminar is based on an older version of Substrate, and [Clap](https://docs.rs/clap/latest/clap/)
//! > is now used instead of [StructOpt](https://docs.rs/structopt/latest/structopt/) for parsing
//! > CLI arguments.
@@ -0,0 +1,27 @@
//! # Custom Host Functions
//!
//! Host functions are functions that the wasm instance can use to communicate with the node. Learn
//! more about this in [`crate::reference_docs::wasm_meta_protocol`].
//!
//! ## Finding Host Functions
//!
//! To declare a set of functions as host functions, you need to use the `#[runtime_interface]`
//! ([`sp_runtime_interface`]) attribute macro. The most notable set of host functions are those
//! that allow the runtime to access the chain state, namely [`sp_io::storage`]. Some other notable
//! host functions are also defined in [`sp_io`].
//!
//! ## Adding New Host Functions
//!
//! > Adding a new host function is a big commitment and should be done with care. Namely, the nodes
//! > in the network need to support all host functions forever in order to be able to sync
//! > historical blocks.
//!
//! Adding host functions is only possible when you are using a node-template, so that you have
//! access to the boilerplate of building your node.
//!
//! A group of host functions can always be grouped to gether as a tuple:
#![doc = docify::embed!("../../substrate/primitives/io/src/lib.rs", SubstrateHostFunctions)]
//!
//! The host functions are attached to the node side's [`sc_executor::WasmExecutor`]. For example in
//! the minimal template, the setup looks as follows:
#![doc = docify::embed!("../../templates/minimal/node/src/service.rs", FullClient)]
@@ -0,0 +1,77 @@
//! # Custom RPC do's and don'ts
//!
//! **TLDR:** Don't create new custom RPCs. Instead, rely on custom Runtime APIs, combined with
//! `state_call`.
//!
//! ## Background
//!
//! Pezkuwi-SDK offers the ability to query and subscribe storages directly. However what it does
//! not have is [view functions](https://github.com/pezkuwichain/pezkuwi-sdk/issues/101). This is an
//! essential feature to avoid duplicated logic between runtime and the client SDK. Custom RPC was
//! used as a solution. It allow the RPC node to expose new RPCs that clients can be used to query
//! computed properties.
//!
//! ## Problems with Custom RPC
//!
//! Unfortunately, custom RPC comes with many problems. To list a few:
//!
//! - It is offchain logic executed by the RPC node and therefore the client has to trust the RPC
//! node.
//! - To upgrade or add a new RPC logic, the RPC node has to be upgraded. This can cause significant
//! trouble when the RPC infrastructure is decentralized as we will need to coordinate multiple
//! parties to upgrade the RPC nodes.
//! - A lot of boilerplate code is required to add custom RPC.
//! - It prevents dApps from using a light client or an alternative client.
//! - It makes ecosystem tooling integration much more complicated. For example, dApps will not
//! be able to use [Chopsticks](https://github.com/AcalaNetwork/chopsticks) for testing as
//! Chopsticks will not have the custom RPC implementation.
//! - Poorly implemented custom RPC can be a DoS vector.
//!
//! Hence, we should avoid custom RPC.
//!
//! ## Alternatives
//!
//! Generally, [`sc_rpc::state::StateBackend::call`] aka. `state_call` should be used instead of
//! custom RPC.
//!
//! Usually, each custom RPC comes with a corresponding runtime API which implements the business
//! logic. So instead of invoke the custom RPC, we can use `state_call` to invoke the runtime API
//! directly. This is a trivial change on the dApp and no change on the runtime side. We may remove
//! the custom RPC from the node side if wanted.
//!
//! There are some other cases that a simple runtime API is not enough. For example, implementation
//! of Ethereum RPC requires an additional offchain database to index transactions. In this
//! particular case, we can have the RPC implemented on another client.
//!
//! For example, the Acala EVM+ RPC are implemented by
//! [eth-rpc-adapter](https://github.com/AcalaNetwork/bodhi.js/tree/master/packages/eth-rpc-adapter).
//! Alternatively, the [Frontier](https://github.com/polkadot-evm/frontier) project also provided
//! Ethereum RPC compatibility directly in the node-side software.
//!
//! ## Create a new Runtime API
//!
//! For example, let's take a look at the process through which the account nonce can be queried
//! through an RPC. First, a new runtime-api needs to be declared:
#![doc = docify::embed!("../../substrate/frame/system/rpc/runtime-api/src/lib.rs", AccountNonceApi)]
//!
//! This API is implemented at the runtime level, always inside [`sp_api::impl_runtime_apis!`].
//!
//! As noted, this is already enough to make this API usable via `state_call`.
//!
//! ## Create a new custom RPC (Legacy)
//!
//! Should you wish to implement the legacy approach of exposing this runtime-api as a custom
//! RPC-api, then a custom RPC server has to be defined.
#![doc = docify::embed!("../../substrate/utils/frame/rpc/system/src/lib.rs", SystemApi)]
//!
//! ## Add a new RPC to the node (Legacy)
//!
//! Finally, this custom RPC needs to be integrated into the node side. This is usually done in a
//! `rpc.rs` in a typical template, as follows:
#![doc = docify::embed!("../../templates/minimal/node/src/rpc.rs", create_full)]
//!
//! ## Future
//!
//! - [XCQ](https://forum.polkadot.network/t/cross-consensus-query-language-xcq/7583) will be a good
//! solution for most of the query needs.
//! - [New JSON-RPC Specification](https://github.com/paritytech/json-rpc-interface-spec)
@@ -0,0 +1,395 @@
// 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.
//! [Defensive programming](https://en.wikipedia.org/wiki/Defensive_programming) is a design paradigm that enables a program to continue
//! running despite unexpected behavior, input, or events that may arise in runtime.
//! Usually, unforeseen circumstances may cause the program to stop or, in the Rust context,
//! `panic!`. Defensive practices allow for these circumstances to be accounted for ahead of time
//! and for them to be handled gracefully, which is in line with the intended fault-tolerant and
//! deterministic nature of blockchains.
//!
//! The Pezkuwi SDK is built to reflect these principles and to facilitate their usage accordingly.
//!
//! ## General Overview
//!
//! When developing within the context of the Substrate runtime, there is one golden rule:
//!
//! ***DO NOT PANIC***. There are some exceptions, but generally, this is the default precedent.
//!
//! > Its important to differentiate between the runtime and node. The runtime refers to the core
//! > business logic of a Substrate-based chain, whereas the node refers to the outer client, which
//! > deals with telemetry and gossip from other nodes. For more information, read about
//! > [Substrate's node
//! > architecture](crate::reference_docs::wasm_meta_protocol#node-vs-runtime). Its also important
//! > to note that the criticality of the node is slightly lesser
//! > than that of the runtime, which is why you may see `unwrap()` or other “non-defensive”
//! > approaches
//! in a few places of the node's code repository.
//!
//! Most of these practices fall within Rust's
//! colloquial usage of proper error propagation, handling, and arithmetic-based edge cases.
//!
//! General guidelines:
//!
//! - **Avoid writing functions that could explicitly panic,** such as directly using `unwrap()` on
//! a [`Result`], or accessing an out-of-bounds index on a collection. Safer methods to access
//! collection types, i.e., `get()` which allow defensive handling of the resulting [`Option`] are
//! recommended to be used.
//! - **It may be acceptable to use `except()`,** but only if one is completely certain (and has
//! performed a check beforehand) that a value won't panic upon unwrapping. *Even this is
//! discouraged*, however, as future changes to that function could then cause that statement to
//! panic. It is important to ensure all possible errors are propagated and handled effectively.
//! - **If a function *can* panic,** it usually is prefaced with `unchecked_` to indicate its
//! unsafety.
//! - **If you are writing a function that could panic,** [document it!](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html#documenting-components)
//! - **Carefully handle mathematical operations.** Many seemingly, simplistic operations, such as
//! **arithmetic** in the runtime, could present a number of issues [(see more later in this
//! document)](#integer-overflow). Use checked arithmetic wherever possible.
//!
//! These guidelines could be summarized in the following example, where `bad_pop` is prone to
//! panicking, and `good_pop` allows for proper error handling to take place:
//!
//!```ignore
//! // Bad pop always requires that we return something, even if vector/array is empty.
//! fn bad_pop<T>(v: Vec<T>) -> T {}
//! // Good pop allows us to return None from the Option if need be.
//! fn good_pop<T>(v: Vec<T>) -> Option<T> {}
//! ```
//!
//! ### Defensive Traits
//!
//! The [`Defensive`](frame::traits::Defensive) trait provides a number of functions, all of which
//! provide an alternative to 'vanilla' Rust functions, e.g.:
//!
//! - [`defensive_unwrap_or()`](frame::traits::Defensive::defensive_unwrap_or) instead of
//! `unwrap_or()`
//! - [`defensive_ok_or()`](frame::traits::DefensiveOption::defensive_ok_or) instead of `ok_or()`
//!
//! Defensive methods use [`debug_assertions`](https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions), which panic in development, but in
//! production/release, they will merely log an error (i.e., `log::error`).
//!
//! The [`Defensive`](frame::traits::Defensive) trait and its various implementations can be found
//! [here](frame::traits::Defensive).
//!
//! ## Integer Overflow
//!
//! The Rust compiler prevents static overflow from happening at compile time.
//! The compiler panics in **debug** mode in the event of an integer overflow. In
//! **release** mode, it resorts to silently _wrapping_ the overflowed amount in a modular fashion
//! (from the `MAX` back to zero).
//!
//! In runtime development, we don't always have control over what is being supplied
//! as a parameter. For example, even this simple add function could present one of two outcomes
//! depending on whether it is in **release** or **debug** mode:
//!
//! ```ignore
//! fn naive_add(x: u8, y: u8) -> u8 {
//! x + y
//! }
//! ```
//! If we passed overflow-able values at runtime, this could panic (or wrap if in release).
//!
//! ```ignore
//! naive_add(250u8, 10u8); // In debug mode, this would panic. In release, this would return 4.
//! ```
//!
//! It is the silent portion of this behavior that presents a real issue. Such behavior should be
//! made obvious, especially in blockchain development, where unsafe arithmetic could produce
//! unexpected consequences like a user balance over or underflowing.
//!
//! Fortunately, there are ways to both represent and handle these scenarios depending on our
//! specific use case natively built into Rust and libraries like [`sp_arithmetic`].
//!
//! ## Infallible Arithmetic
//!
//! Both Rust and Substrate provide safe ways to deal with numbers and alternatives to floating
//! point arithmetic.
//!
//! Known scenarios that could be fallible should be avoided: i.e., avoiding the possibility of
//! dividing/modulo by zero at any point should be mitigated. One should be opting for a
//! `checked_*` method to introduce safe arithmetic in their code in most cases.
//!
//! A developer should use fixed-point instead of floating-point arithmetic to mitigate the
//! potential for inaccuracy, rounding errors, or other unexpected behavior.
//!
//! - [Fixed point types](sp_arithmetic::fixed_point) and their associated usage can be found here.
//! - [PerThing](sp_arithmetic::per_things) and its associated types can be found here.
//!
//! Using floating point number types (i.e. f32, f64) in the runtime should be avoided, as a single non-deterministic result could cause chaos for blockchain consensus along with the issues above. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0).
//!
//! The following methods demonstrate different ways to handle numbers natively in Rust safely,
//! without fear of panic or unexpected behavior from wrapping.
//!
//! ### Checked Arithmetic
//!
//! **Checked operations** utilize an `Option<T>` as a return type. This allows for
//! catching any unexpected behavior in the event of an overflow through simple pattern matching.
//!
//! This is an example of a valid operation:
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", checked_add_example)]
//!
//! This is an example of an invalid operation. In this case, a simulated integer overflow, which
//! would simply result in `None`:
#![doc = docify::embed!(
"./src/reference_docs/defensive_programming.rs",
checked_add_handle_error_example
)]
//!
//! Suppose you arent sure which operation to use for runtime math. In that case, checked
//! operations are the safest bet, presenting two predictable (and erroring) outcomes that can be
//! handled accordingly (Some and None).
//!
//! The following conventions can be seen within the Pezkuwi SDK, where it is
//! handled in two ways:
//!
//! - As an [`Option`], using the `if let` / `if` or `match`
//! - As a [`Result`], via `ok_or` (or similar conversion to [`Result`] from [`Option`])
//!
//! #### Handling via Option - More Verbose
//!
//! Because wrapped operations return `Option<T>`, you can use a more verbose/explicit form of error
//! handling via `if` or `if let`:
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance)]
//!
//! Optionally, match may also be directly used in a more concise manner:
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance_match)]
//!
//! This is generally a useful convention for handling checked types and most types that return
//! `Option<T>`.
//!
//! #### Handling via Result - Less Verbose
//!
//! In the Pezkuwi SDK codebase, checked operations are handled as a `Result` via `ok_or`. This is
//! a less verbose way of expressing the above. This usage often boils down to the developers
//! preference:
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", increase_balance_result)]
//!
//! ### Saturating Operations
//!
//! Saturating a number limits it to the types upper or lower bound, even if the integer type
//! overflowed in runtime. For example, adding to `u32::MAX` would simply limit itself to
//! `u32::MAX`:
#![doc = docify::embed!("./src/reference_docs/defensive_programming.rs", saturated_add_example)]
//!
//! Saturating calculations can be used if one is very sure that something won't overflow, but wants
//! to avoid introducing the notion of any potential-panic or wrapping behavior.
//!
//! There is also a series of defensive alternatives via
//! [`DefensiveSaturating`](frame::traits::DefensiveSaturating), which introduces the same behavior
//! of the [`Defensive`](frame::traits::Defensive) trait, only with saturating, mathematical
//! operations:
#![doc = docify::embed!(
"./src/reference_docs/defensive_programming.rs",
saturated_defensive_example
)]
//!
//! ### Mathematical Operations in Substrate Development - Further Context
//!
//! As a recap, we covered the following concepts:
//!
//! 1. **Checked** operations - using [`Option`] or [`Result`]
//! 2. **Saturating** operations - limited to the lower and upper bounds of a number type
//! 3. **Wrapped** operations (the default) - wrap around to above or below the bounds of a type
//!
//! #### The problem with 'default' wrapped operations
//!
//! **Wrapped operations** cause the overflow to wrap around to either the maximum or minimum of
//! that type. Imagine this in the context of a blockchain, where there are account balances, voting
//! counters, nonces for transactions, and other aspects of a blockchain.
//!
//! While it may seem trivial, choosing how to handle numbers is quite important. As a thought
//! exercise, here are some scenarios of which will shed more light on when to use which.
//!
//! #### Bob's Overflowed Balance
//!
//! **Bob's** balance exceeds the `Balance` type on the `EduChain`. Because the pallet developer did
//! not handle the calculation to add to Bob's balance with any regard to this overflow, **Bob's**
//! balance is now essentially `0`, the operation **wrapped**.
//!
//! <details>
//! <summary><b>Solution: Saturating or Checked</b></summary>
//! For Bob's balance problems, using a `saturating_add` or `checked_add` could've mitigated
//! this issue. They simply would've reached the upper, or lower bounds, of the particular type for
//! an on-chain balance. In other words: Bob's balance would've stayed at the maximum of the
//! Balance type. </details>
//!
//! #### Alice's 'Underflowed' Balance
//!
//! Alices balance has reached `0` after a transfer to Bob. Suddenly, she has been slashed on
//! EduChain, causing her balance to reach near the limit of `u32::MAX` - a very large amount - as
//! wrapped operations can go both ways. Alice can now successfully vote using her new, overpowered
//! token balance, destroying the chain's integrity.
//!
//! <details>
//! <summary><b>Solution: Saturating</b></summary>
//! For Alice's balance problem, using `saturated_sub` could've mitigated this issue. A saturating
//! calculation would've simply limited her balance to the lower bound of u32, as having a negative
//! balance is not a concept within blockchains. In other words: Alice's balance would've stayed
//! at "0", even after being slashed.
//!
//! This is also an example that while one system may work in isolation, shared interfaces, such
//! as the notion of balances, are often shared across multiple pallets - meaning these small
//! changes can make a big difference depending on the scenario. </details>
//!
//! #### Proposal ID Overwrite
//!
//! A `u8` parameter, called `proposals_count`, represents the type for counting the number of
//! proposals on-chain. Every time a new proposal is added to the system, this number increases.
//! With the proposal pallet's high usage, it has reached `u8::MAX`s limit of 255, causing
//! `proposals_count` to go to 0. Unfortunately, this results in new proposals overwriting old ones,
//! effectively erasing any notion of past proposals!
//!
//! <details>
//! <summary><b>Solution: Checked</b></summary>
//! For the proposal IDs, proper handling via `checked` math would've been suitable,
//! Saturating could've been used - but it also would've 'failed' silently. Using `checked_add` to
//! ensure that the next proposal ID would've been valid would've been a viable way to let the user
//! know the state of their proposal:
//!
//! ```ignore
//! let next_proposal_id = current_count.checked_add(1).ok_or_else(|| Error::TooManyProposals)?;
//! ```
//!
//! </details>
//!
//! From the above, we can clearly see the problematic nature of seemingly simple operations in the
//! runtime, and care should be given to ensure a defensive approach is taken.
//!
//! ### Edge cases of `panic!`-able instances in Substrate
//!
//! As you traverse through the codebase (particularly in `substrate/frame`, where the majority of
//! runtime code lives), you may notice that there (only a few!) occurrences where `panic!` is used
//! explicitly. This is used when the runtime should stall, rather than keep running, as that is
//! considered safer. Particularly when it comes to mission-critical components, such as block
//! authoring, consensus, or other protocol-level dependencies, going through with an action may
//! actually cause harm to the network, and thus stalling would be the better option.
//!
//! Take the example of the BABE pallet ([`pallet_babe`]), which doesn't allow for a validator to
//! participate if it is disabled (see: [`frame::traits::DisabledValidators`]):
//!
//! ```ignore
//! if T::DisabledValidators::is_disabled(authority_index) {
//! panic!(
//! "Validator with index {:?} is disabled and should not be attempting to author blocks.",
//! authority_index,
//! );
//! }
//! ```
//!
//! There are other examples in various pallets, mostly those crucial to the blockchains
//! functionality. Most of the time, you will not be writing pallets which operate at this level,
//! but these exceptions should be noted regardless.
//!
//! ## Other Resources
//!
//! - [PBA Lectures on YouTube](https://www.youtube.com/playlist?list=PL-w_i5kwVqbni1Ch2j_RwTIXiB-bwnYqq)
#![allow(dead_code)]
#[allow(unused_variables)]
mod fake_runtime_types {
// Note: The following types are purely for the purpose of example, and do not contain any
// *real* use case other than demonstrating various concepts.
pub enum RuntimeError {
Overflow,
UserDoesntExist,
}
pub type Address = ();
pub struct Runtime;
impl Runtime {
fn get_balance(account: Address) -> Result<u64, RuntimeError> {
Ok(0u64)
}
fn set_balance(account: Address, new_balance: u64) {}
}
#[docify::export]
fn increase_balance(account: Address, amount: u64) -> Result<(), RuntimeError> {
// Get a user's current balance
let balance = Runtime::get_balance(account)?;
// SAFELY increase the balance by some amount
if let Some(new_balance) = balance.checked_add(amount) {
Runtime::set_balance(account, new_balance);
Ok(())
} else {
Err(RuntimeError::Overflow)
}
}
#[docify::export]
fn increase_balance_match(account: Address, amount: u64) -> Result<(), RuntimeError> {
// Get a user's current balance
let balance = Runtime::get_balance(account)?;
// SAFELY increase the balance by some amount
let new_balance = match balance.checked_add(amount) {
Some(balance) => balance,
None => {
return Err(RuntimeError::Overflow);
},
};
Runtime::set_balance(account, new_balance);
Ok(())
}
#[docify::export]
fn increase_balance_result(account: Address, amount: u64) -> Result<(), RuntimeError> {
// Get a user's current balance
let balance = Runtime::get_balance(account)?;
// SAFELY increase the balance by some amount - this time, by using `ok_or`
let new_balance = balance.checked_add(amount).ok_or(RuntimeError::Overflow)?;
Runtime::set_balance(account, new_balance);
Ok(())
}
}
#[cfg(test)]
mod tests {
use frame::traits::DefensiveSaturating;
#[docify::export]
#[test]
fn checked_add_example() {
// This is valid, as 20 is perfectly within the bounds of u32.
let add = (10u32).checked_add(10);
assert_eq!(add, Some(20))
}
#[docify::export]
#[test]
fn checked_add_handle_error_example() {
// This is invalid - we are adding something to the max of u32::MAX, which would overflow.
// Luckily, checked_add just marks this as None!
let add = u32::MAX.checked_add(10);
assert_eq!(add, None)
}
#[docify::export]
#[test]
fn saturated_add_example() {
// Saturating add simply saturates
// to the numeric bound of that type if it overflows.
let add = u32::MAX.saturating_add(10);
assert_eq!(add, u32::MAX)
}
#[docify::export]
#[test]
#[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))]
fn saturated_defensive_example() {
let saturated_defensive = u32::MAX.defensive_saturating_add(10);
assert_eq!(saturated_defensive, u32::MAX);
}
}
@@ -0,0 +1,223 @@
//! # Development Environment Advice
//!
//! Large Rust projects are known for sometimes long compile times and sluggish dev tooling, and
//! pezkuwi-sdk is no exception.
//!
//! This page contains some advice to improve your workflow when using common tooling.
//!
//! ## Rust Analyzer Configuration
//!
//! [Rust Analyzer](https://rust-analyzer.github.io/) is the defacto [LSP](https://langserver.org/) for Rust. Its default
//! settings are fine for smaller projects, but not well optimised for pezkuwi-sdk.
//!
//! Below is a suggested configuration for VSCode or any VSCode-based editor like Cursor:
//!
//! ```json
//! {
//! // Use a separate target dir for Rust Analyzer. Helpful if you want to use Rust
//! // Analyzer and cargo on the command line at the same time,
//! // at the expense of duplicating build artifacts.
//! "rust-analyzer.cargo.targetDir": "target/vscode-rust-analyzer",
//! // Improve stability
//! "rust-analyzer.server.extraEnv": {
//! "CHALK_OVERFLOW_DEPTH": "100000000",
//! "CHALK_SOLVER_MAX_SIZE": "10000000"
//! },
//! // Check feature-gated code
//! "rust-analyzer.cargo.features": "all",
//! "rust-analyzer.cargo.extraEnv": {
//! // Skip building WASM, there is never need for it here
//! "SKIP_WASM_BUILD": "1"
//! },
//! // Don't expand some problematic proc_macros
//! "rust-analyzer.procMacro.ignored": {
//! "async-trait": ["async_trait"],
//! "napi-derive": ["napi"],
//! "async-recursion": ["async_recursion"],
//! "async-std": ["async_std"]
//! },
//! // Use nightly formatting.
//! // See the pezkuwi-sdk CI job that checks formatting for the current version used in
//! // pezkuwi-sdk.
//! "rust-analyzer.rustfmt.extraArgs": ["+nightly-2024-04-10"],
//! }
//! ```
//!
//! and the same in Lua for `neovim/nvim-lspconfig`:
//!
//! ```lua
//! ["rust-analyzer"] = {
//! rust = {
//! # Use a separate target dir for Rust Analyzer. Helpful if you want to use Rust
//! # Analyzer and cargo on the command line at the same time.
//! analyzerTargetDir = "target/nvim-rust-analyzer",
//! },
//! server = {
//! # Improve stability
//! extraEnv = {
//! ["CHALK_OVERFLOW_DEPTH"] = "100000000",
//! ["CHALK_SOLVER_MAX_SIZE"] = "100000000",
//! },
//! },
//! cargo = {
//! # Check feature-gated code
//! features = "all",
//! extraEnv = {
//! # Skip building WASM, there is never need for it here
//! ["SKIP_WASM_BUILD"] = "1",
//! },
//! },
//! procMacro = {
//! # Don't expand some problematic proc_macros
//! ignored = {
//! ["async-trait"] = { "async_trait" },
//! ["napi-derive"] = { "napi" },
//! ["async-recursion"] = { "async_recursion" },
//! ["async-std"] = { "async_std" },
//! },
//! },
//! rustfmt = {
//! # Use nightly formatting.
//! # See the pezkuwi-sdk CI job that checks formatting for the current version used in
//! # pezkuwi-sdk.
//! extraArgs = { "+nightly-2024-04-10" },
//! },
//! },
//! ```
//!
//! Alternatively for neovim, if you are using [Rustaceanvim](https://github.com/mrcjkb/rustaceanvim),
//! you can achieve the same configuring `rust-analyzer` via `rustaceanvim` as follows:
//! ```lua
//! return {
//! {
//! "mrcjkb/rustaceanvim",
//! opts = {
//! server = {
//! default_settings = {
//! ["rust-analyzer"] = {
//! // put the same config as for nvim-lspconfig here
//! },
//! },
//! },
//! },
//! },
//! }
//! ```
//!
//! Similarly for Zed, you can replicate the same VSCode configuration in
//! `~/.config/zed/settings.json` as follows:
//! ```json
//! "lsp": {
//! "rust-analyzer": {
//! "initialization_options": {
//! // same config as for VSCode for rust, cargo, procMacros, ...
//! }
//! }
//! },
//! ```
//!
//! In general, refer to your favorite editor / IDE's documentation to properly configure
//! `rust-analyzer` as language server.
//! For the full set of configuration options see <https://rust-analyzer.github.io/manual.html#configuration>.
//!
//! ## Cargo Usage
//!
//! ### Using `--package` (a.k.a. `-p`)
//!
//! pezkuwi-sdk is a monorepo containing many crates. When you run a cargo command without
//! `-p`, you will almost certainly compile crates outside of the scope you are working.
//!
//! Instead, you should identify the name of the crate you are working on by checking the `name`
//! field in the closest `Cargo.toml` file. Then, use `-p` with your cargo commands to only compile
//! that crate.
//!
//! ### `SKIP_WASM_BUILD=1` environment variable
//!
//! When cargo touches a runtime crate, by default it will also compile the WASM binary,
//! approximately doubling the compilation time.
//!
//! The WASM binary is usually not needed, especially when running `check` or `test`. To skip the
//! WASM build, set the `SKIP_WASM_BUILD` environment variable to `1`. For example:
//! `SKIP_WASM_BUILD=1 cargo check -p frame-support`.
//!
//! ### Cargo Remote
//!
//! Warning: cargo remote by default doesn't transfer hidden files to the remote machine. But hidden
//! files can be useful, e.g. for sqlx usage. On the other hand using `--transfer-hidden` flag will
//! transfer `.git` which is big.
//!
//! If you have a powerful remote server available, you may consider using
//! [cargo-remote](https://github.com/sgeisler/cargo-remote) to execute cargo commands on it,
//! freeing up local resources for other tasks like `rust-analyzer`.
//!
//! When using `cargo-remote`, you can configure your editor to perform the the typical
//! "check-on-save" remotely as well. The configuration for VSCode (or any VSCode-based editor like
//! Cursor) is as follows:
//!
//! ```json
//! {
//! "rust-analyzer.cargo.buildScripts.overrideCommand": [
//! "cargo",
//! "remote",
//! "--build-env",
//! "SKIP_WASM_BUILD=1",
//! "--",
//! "check",
//! "--message-format=json",
//! "--all-targets",
//! "--all-features",
//! "--target-dir=target/rust-analyzer"
//! ],
//! "rust-analyzer.check.overrideCommand": [
//! "cargo",
//! "remote",
//! "--build-env",
//! "SKIP_WASM_BUILD=1",
//! "--",
//! "check",
//! "--workspace",
//! "--message-format=json",
//! "--all-targets",
//! "--all-features",
//! "--target-dir=target/rust-analyzer"
//! ],
//! }
//! ```
//!
//! and the same in Lua for `neovim/nvim-lspconfig`:
//!
//! ```lua
//! ["rust-analyzer"] = {
//! cargo = {
//! buildScripts = {
//! overrideCommand = {
//! "cargo",
//! "remote",
//! "--build-env",
//! "SKIP_WASM_BUILD=1",
//! "--",
//! "check",
//! "--message-format=json",
//! "--all-targets",
//! "--all-features",
//! "--target-dir=target/rust-analyzer"
//! },
//! },
//! },
//! check = {
//! overrideCommand = {
//! "cargo",
//! "remote",
//! "--build-env",
//! "SKIP_WASM_BUILD=1",
//! "--",
//! "check",
//! "--workspace",
//! "--message-format=json",
//! "--all-targets",
//! "--all-features",
//! "--target-dir=target/rust-analyzer"
//! },
//! },
//! },
//! ```
@@ -0,0 +1,333 @@
//! # Constructing and Signing Extrinsics
//!
//! Extrinsics are payloads that are stored in blocks which are responsible for altering the state
//! of a blockchain via the [_state transition
//! function_][crate::reference_docs::blockchain_state_machines].
//!
//! Substrate is configurable enough that extrinsics can take any format. In practice, runtimes
//! tend to use our [`sp_runtime::generic::UncheckedExtrinsic`] type to represent extrinsics,
//! because it's generic enough to cater for most (if not all) use cases. In Pezkuwi, this is
//! configured [here](https://github.com/polkadot-fellows/runtimes/blob/94b2798b69ba6779764e20a50f056e48db78ebef/relay/polkadot/src/lib.rs#L1478)
//! at the time of writing.
//!
//! What follows is a description of how extrinsics based on this
//! [`sp_runtime::generic::UncheckedExtrinsic`] type are encoded into bytes. Specifically, we are
//! looking at how extrinsics with a format version of 5 are encoded. This version is itself a part
//! of the payload, and if it changes, it indicates that something about the encoding may have
//! changed.
//!
//! # Encoding an Extrinsic
//!
//! At a high level, all extrinsics compatible with [`sp_runtime::generic::UncheckedExtrinsic`]
//! are formed from concatenating some details together, as in the following pseudo-code:
//!
//! ```text
//! extrinsic_bytes = concat(
//! compact_encoded_length,
//! version_and_extrinsic_type,
//! maybe_extension_data,
//! call_data
//! )
//! ```
//!
//! For clarity, the actual implementation in Substrate looks like this:
#![doc = docify::embed!("../../substrate/primitives/runtime/src/generic/unchecked_extrinsic.rs", unchecked_extrinsic_encode_impl)]
//!
//! Let's look at how each of these details is constructed:
//!
//! ## compact_encoded_length
//!
//! This is a [SCALE compact encoded][frame::deps::codec::Compact] integer which is equal to the
//! length, in bytes, of the rest of the extrinsic details.
//!
//! To obtain this value, we must encode and concatenate together the rest of the extrinsic details
//! first, and then obtain the byte length of these. We can then compact encode that length, and
//! prepend it to the rest of the details.
//!
//! ## version_and_maybe_signature
//!
//! If the extrinsic is _unsigned_, then `version_and_maybe_signature` will be just one byte
//! denoting the _transaction protocol version_, which is 4 (or `0b0000_0100`).
//!
//! If the extrinsic is _signed_ (all extrinsics submitted from users must be signed), then
//! `version_and_maybe_signature` is obtained by concatenating some details together, ie:
//!
//! ```text
//! version_and_maybe_signature = concat(
//! version_and_signed,
//! from_address,
//! signature,
//! transaction_extensions_extra,
//! )
//! ```
//!
//! Each of the details to be concatenated together is explained below:
//!
//! ## version_and_extrinsic_type
//!
//! This byte has 2 components:
//! - the 2 most significant bits represent the extrinsic type:
//! - bare - `0b00`
//! - signed - `0b10`
//! - general - `0b01`
//! - the 6 least significant bits represent the extrinsic format version (currently 5)
//!
//! ### Bare extrinsics
//!
//! If the extrinsic is _bare_, then `version_and_extrinsic_type` will be just the _transaction
//! protocol version_, which is 5 (or `0b0000_0101`). Bare extrinsics do not carry any other
//! extension data, so `maybe_extension_data` would not be included in the payload and the
//! `version_and_extrinsic_type` would always be followed by the encoded call bytes.
//!
//! ### Signed extrinsics
//!
//! If the extrinsic is _signed_ (all extrinsics submitted from users used to be signed up until
//! version 4), then `version_and_extrinsic_type` is obtained by having a MSB of `1` on the
//! _transaction protocol version_ byte (which translates to `0b1000_0101`).
//!
//! Additionally, _signed_ extrinsics also carry with them address and signature information encoded
//! as follows:
//!
//! #### from_address
//!
//! This is the [SCALE encoded][frame::deps::codec] address of the sender of the extrinsic. The
//! address is the first generic parameter of [`sp_runtime::generic::UncheckedExtrinsic`], and so
//! can vary from chain to chain.
//!
//! The address type used on the Pezkuwi relay chain is [`sp_runtime::MultiAddress<AccountId32>`],
//! where `AccountId32` is defined [here][`sp_core::crypto::AccountId32`]. When constructing a
//! signed extrinsic to be submitted to a Pezkuwi node, you'll always use the
//! [`sp_runtime::MultiAddress::Id`] variant to wrap your `AccountId32`.
//!
//! #### signature
//!
//! This is the [SCALE encoded][frame::deps::codec] signature. The signature type is configured via
//! the third generic parameter of [`sp_runtime::generic::UncheckedExtrinsic`], which determines the
//! shape of the signature and signing algorithm that should be used.
//!
//! The signature is obtained by signing the _signed payload_ bytes (see below on how this is
//! constructed) using the private key associated with the address and correct algorithm.
//!
//! The signature type used on the Pezkuwi relay chain is [`sp_runtime::MultiSignature`]; the
//! variants there are the types of signature that can be provided.
//!
//! ### General extrinsics
//!
//! If the extrinsic is _general_ (it doesn't carry a signature in the payload, only extension
//! data), then `version_and_extrinsic_type` is obtained by logical OR between the general
//! transaction type bits and the _transaction protocol version_ byte (which translates to
//! `0b0100_0101`).
//!
//! ### transaction_extensions_extra
//!
//! This is the concatenation of the [SCALE encoded][frame::deps::codec] bytes representing first a
//! single byte describing the extension version (this is bumped whenever a change occurs in the
//! transaction extension pipeline) followed by the bytes of each of the [_transaction
//! extensions_][sp_runtime::traits::TransactionExtension], and are configured by the fourth generic
//! parameter of [`sp_runtime::generic::UncheckedExtrinsic`]. Learn more about transaction
//! extensions [here][crate::reference_docs::transaction_extensions].
//!
//! When it comes to constructing an extrinsic, each transaction extension has two things that we
//! are interested in here:
//!
//! - The actual SCALE encoding of the transaction extension type itself; this is what will form our
//! `transaction_extensions_extra` bytes.
//! - An `Implicit` type. This is SCALE encoded into the `transaction_extensions_implicit` data (see
//! below).
//!
//! Either (or both) of these can encode to zero bytes.
//!
//! Each chain configures the set of transaction extensions that it uses in its runtime
//! configuration. At the time of writing, Pezkuwi configures them
//! [here](https://github.com/polkadot-fellows/runtimes/blob/1dc04eb954eadf8aadb5d83990b89662dbb5a074/relay/polkadot/src/lib.rs#L1432C25-L1432C25).
//! Some of the common transaction extensions are defined
//! [here][frame::deps::frame_system#transaction-extensions].
//!
//! Information about exactly which transaction extensions are present on a chain and in what order
//! is also a part of the metadata for the chain. For V15 metadata, it can be [found
//! here][frame::deps::frame_support::__private::metadata::v15::ExtrinsicMetadata].
//!
//! ## call_data
//!
//! This is the main payload of the extrinsic, which is used to determine how the chain's state is
//! altered. This is defined by the second generic parameter of
//! [`sp_runtime::generic::UncheckedExtrinsic`].
//!
//! A call can be anything that implements [`Encode`][frame::deps::codec::Encode]. In FRAME-based
//! runtimes, a call is represented as an enum of enums, where the outer enum represents the FRAME
//! pallet being called, and the inner enum represents the call being made within that pallet, and
//! any arguments to it. Read more about the call enum
//! [here][crate::reference_docs::frame_runtime_types].
//!
//! FRAME `Call` enums are automatically generated, and end up looking something like this:
#![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", call_data)]
//!
//! In pseudo-code, this `Call` enum encodes equivalently to:
//!
//! ```text
//! call_data = concat(
//! pallet_index,
//! call_index,
//! call_args
//! )
//! ```
//!
//! - `pallet_index` is a single byte denoting the index of the pallet that we are calling into, and
//! is what the tag of the outermost enum will encode to.
//! - `call_index` is a single byte denoting the index of the call that we are making the pallet,
//! and is what the tag of the inner enum will encode to.
//! - `call_args` are the SCALE encoded bytes for each of the arguments that the call expects, and
//! are typically provided as values to the inner enum.
//!
//! Information about the pallets that exist for a chain (including their indexes), the calls
//! available in each pallet (including their indexes), and the arguments required for each call can
//! be found in the metadata for the chain. For V15 metadata, this information [is
//! here][frame::deps::frame_support::__private::metadata::v15::PalletMetadata].
//!
//! # The Signed Payload Format
//!
//! All _signed_ extrinsics submitted to a node from the outside world (also known as
//! _transactions_) need to be _signed_. The data that needs to be signed for some extrinsic is
//! called the _signed payload_, and its shape is described by the following pseudo-code:
//!
//! ```text
//! signed_payload = blake2_256(
//! concat(
//! call_data,
//! transaction_extensions_extra,
//! transaction_extensions_implicit,
//! )
//! )
//! ```
//!
//! The bytes representing `call_data` and `transaction_extensions_extra` can be obtained as
//! descibed above. `transaction_extensions_implicit` is constructed by SCALE encoding the
//! ["implicit" data][sp_runtime::traits::TransactionExtension::Implicit] for each transaction
//! extension that the chain is using, in order.
//!
//! Once we've concatenated those together, we hash the result using a Blake2 256bit hasher.
//!
//! The [`sp_runtime::generic::SignedPayload`] type takes care of assembling the correct payload for
//! us, given `call_data` and a tuple of transaction extensions.
//!
//! # The General Transaction Format
//!
//! A General transaction does not have a signature method hardcoded in the check logic of the
//! extrinsic, such as a traditionally signed transaction. Instead, general transactions should have
//! one or more extensions in the transaction extension pipeline that auhtorize origins in some way,
//! one of which could be the traditional signature check that happens for all signed transactions
//! in the [Checkable](sp_runtime::traits::Checkable) implementation of
//! [UncheckedExtrinsic](sp_runtime::generic::UncheckedExtrinsic). Therefore, it is up to each
//! extension to define the format of the payload it will try to check and authorize the right
//! origin type. For an example, look into the [authorization example pallet
//! extensions](pallet_example_authorization_tx_extension::extensions)
//!
//! # Example Encoding
//!
//! Using [`sp_runtime::generic::UncheckedExtrinsic`], we can construct and encode an extrinsic as
//! follows:
#![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", encoding_example)]
#[docify::export]
pub mod call_data {
use codec::{Decode, Encode};
use sp_runtime::{traits::Dispatchable, DispatchResultWithInfo};
// The outer enum composes calls within
// different pallets together. We have two
// pallets, "PalletA" and "PalletB".
#[derive(Encode, Decode, Clone)]
pub enum Call {
#[codec(index = 0)]
PalletA(PalletACall),
#[codec(index = 7)]
PalletB(PalletBCall),
}
// An inner enum represents the calls within
// a specific pallet. "PalletA" has one call,
// "Foo".
#[derive(Encode, Decode, Clone)]
pub enum PalletACall {
#[codec(index = 0)]
Foo(String),
}
#[derive(Encode, Decode, Clone)]
pub enum PalletBCall {
#[codec(index = 0)]
Bar(String),
}
impl Dispatchable for Call {
type RuntimeOrigin = ();
type Config = ();
type Info = ();
type PostInfo = ();
fn dispatch(self, _origin: Self::RuntimeOrigin) -> DispatchResultWithInfo<Self::PostInfo> {
Ok(())
}
}
}
#[docify::export]
pub mod encoding_example {
use super::call_data::{Call, PalletACall};
use crate::reference_docs::transaction_extensions::transaction_extensions_example;
use codec::Encode;
use sp_core::crypto::AccountId32;
use sp_keyring::sr25519::Keyring;
use sp_runtime::{
generic::{SignedPayload, UncheckedExtrinsic},
MultiAddress, MultiSignature,
};
// Define some transaction extensions to use. We'll use a couple of examples
// from the transaction extensions reference doc.
type TransactionExtensions = (
transaction_extensions_example::AddToPayload,
transaction_extensions_example::AddToSignaturePayload,
);
// We'll use `UncheckedExtrinsic` to encode our extrinsic for us. We set
// the address and signature type to those used on Pezkuwi, use our custom
// `Call` type, and use our custom set of `TransactionExtensions`.
type Extrinsic = UncheckedExtrinsic<
MultiAddress<AccountId32, ()>,
Call,
MultiSignature,
TransactionExtensions,
>;
pub fn encode_demo_extrinsic() -> Vec<u8> {
// The "from" address will be our Alice dev account.
let from_address = MultiAddress::<AccountId32, ()>::Id(Keyring::Alice.to_account_id());
// We provide some values for our expected transaction extensions.
let transaction_extensions = (
transaction_extensions_example::AddToPayload(1),
transaction_extensions_example::AddToSignaturePayload,
);
// Construct our call data:
let call_data = Call::PalletA(PalletACall::Foo("Hello".to_string()));
// The signed payload. This takes care of encoding the call_data,
// transaction_extensions_extra and transaction_extensions_implicit, and hashing
// the result if it's > 256 bytes:
let signed_payload = SignedPayload::new(call_data.clone(), transaction_extensions.clone());
// Sign the signed payload with our Alice dev account's private key,
// and wrap the signature into the expected type:
let signature = {
let sig = Keyring::Alice.sign(&signed_payload.encode());
MultiSignature::Sr25519(sig)
};
// Now, we can build and encode our extrinsic:
let ext = Extrinsic::new_signed(call_data, from_address, signature, transaction_extensions);
let encoded_ext = ext.encode();
encoded_ext
}
}
@@ -0,0 +1,13 @@
//! # Fee-Less Runtime
//!
//! 🚧 Work In Progress 🚧
//!
//! Notes:
//!
//! - An extension of [`runtime_vs_smart_contract`], showcasing the tools needed to build a safe
//! runtime that is fee-less.
//! - Would need to use unsigned origins, custom validate_unsigned, check the existence of some NFT
//! and some kind of rate limiting (eg. any account gets 5 free tx per day).
//! - The rule of thumb is that as long as the unsigned validate does one storage read, similar to
//! nonce, it is fine.
//! - This could possibly be a good guide/template, rather than a reference doc.
@@ -0,0 +1,212 @@
//! # FRAME Benchmarking and Weights.
//!
//! This reference doc explores the concept of weights within Pezkuwi-SDK runtimes, and more
//! specifically how FRAME-based runtimes handle it.
//!
//! ## Metering
//!
//! The existence of "weight" as a concept in Pezkuwi-SDK is a direct consequence of the usage of
//! WASM as a virtual machine. Unlike a metered virtual machine like EVM, where every instruction
//! can have a (fairly) deterministic "cost" (also known as "gas price") associated with it, WASM is
//! a stack machine with more complex instruction set, and more unpredictable execution times. This
//! means that unlike EVM, it is not possible to implement a "metering" system in WASM. A metering
//! system is one in which instructions are executed one by one, and the cost/gas is stored in an
//! accumulator. The execution may then halt once a gas limit is reached.
//!
//! In Pezkuwi-SDK, the WASM runtime is not assumed to be metered.
//!
//! ## Trusted Code
//!
//! Another important difference is that EVM is mostly used to express smart contracts, which are
//! foreign and untrusted codes from the perspective of the blockchain executing them. In such
//! cases, metering is crucial, in order to ensure a malicious code cannot consume more gas than
//! expected.
//!
//! This assumption does not hold about the runtime of Pezkuwi-SDK-based blockchains. The runtime
//! is trusted code, and it is assumed to be written by the same team/developers who are running the
//! blockchain itself. Therefore, this assumption of "untrusted foreign code" does not hold.
//!
//! This is why the runtime can opt for a more performant, more flexible virtual machine like WASM,
//! and get away without having metering.
//!
//! ## Benchmarking
//!
//! With the matter of untrusted code execution out of the way, the need for strict metering goes
//! out of the way. Yet, it would still be very beneficial for block producers to be able to know an
//! upper bound on how much resources a operation is going to consume before actually executing that
//! operation. This is why FRAME has a toolkit for benchmarking pallets: So that this upper bound
//! can be empirically determined.
//!
//! > Note: Benchmarking is a static analysis: It is all about knowing the upper bound of how much
//! > resources an operation takes statically, without actually executing it. In the context of
//! > FRAME extrinsics, this static-ness is expressed by the keyword "pre-dispatch".
//!
//! To understand why this upper bound is needed, consider the following: A block producer knows
//! they have 20ms left to finish producing their block, and wishes to include more transactions in
//! the block. Yet, in a metered environment, it would not know which transaction is likely to fit
//! the 20ms. In a benchmarked environment, it can examine the transactions for their upper bound,
//! and include the ones that are known to fit based on the worst case.
//!
//! The benchmarking code can be written as a part of FRAME pallet, using the macros provided in
//! [`frame_benchmarking`]. See any of the existing pallets in `pezkuwi-sdk`, or the pallets in our
//! [`crate::pezkuwi_sdk::templates`] for examples.
//!
//! ## Weight
//!
//! Finally, [`sp_weights::Weight`] is the output of the benchmarking process. It is a
//! two-dimensional data structure that demonstrates the resources consumed by a given block of
//! code (for example, a transaction). The two dimensions are:
//!
//! * reference time: The time consumed in pico-seconds, on a reference hardware.
//! * proof size: The amount of storage proof necessary to re-execute the block of code. This is
//! mainly needed for teyrchain <> relay-chain verification.
//!
//! ## How To Write Benchmarks: Worst Case
//!
//! The most important detail about writing benchmarking code is that it must be written such that
//! it captures the worst case execution of any block of code.
//!
//! Consider:
#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer)]
//!
//! If this block of code is to be benchmarked, then the benchmarking code must be written such that
//! it captures the worst case.
//!
//! ## Gluing Pallet Benchmarking with Runtime
//!
//! FRAME pallets are mandated to provide their own benchmarking code. Runtimes contain the
//! boilerplate needed to run these benchmarking (see [Running Benchmarks
//! below](#running-benchmarks)). The outcome of running these benchmarks are meant to be fed back
//! into the pallet via a conventional `trait WeightInfo` on `Config`:
#![doc = docify::embed!("src/reference_docs/frame_benchmarking_weight.rs", WeightInfo)]
//!
//! Then, individual functions of this trait are the final values that we assigned to the
//! [`frame::pallet_macros::weight`] attribute:
#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer_2)]
//!
//! ## Manual Refund
//!
//! Back to the assumption of writing benchmarks for worst case: Sometimes, the pre-dispatch weight
//! significantly differ from the post-dispatch actual weight consumed. This can be expressed with
//! the following FRAME syntax:
#![doc = docify::embed!("./src/reference_docs/frame_benchmarking_weight.rs", simple_transfer_3)]
//!
//! ## Running Benchmarks
//!
//! Two ways exist to run the benchmarks of a runtime.
//!
//! 1. The old school way: Most Pezkuwi-SDK based nodes (such as the ones integrated in
//! [`templates`]) have a `benchmark` subcommand integrated into themselves.
//! 2. The more [`crate::reference_docs::omni_node`] compatible way of running the benchmarks would
//! be using [`frame-omni-bencher`] CLI, which only relies on a runtime.
//!
//! Note that by convention, the runtime and pallets always have their benchmarking code feature
//! gated as behind `runtime-benchmarks`. So, the runtime should be compiled with `--features
//! runtime-benchmarks`.
//!
//! ## Automatic Refund of `proof_size`.
//!
//! A new feature in FRAME allows the runtime to be configured for "automatic refund" of the proof
//! size weight. This is very useful for maximizing the throughput of teyrchains. Please see:
//! [`crate::guides::enable_pov_reclaim`].
//!
//! ## Summary
//!
//! Pezkuwi-SDK runtimes use a more performant VM, namely WASM, which does not have metering. In
//! return they have to be benchmarked to provide an upper bound on the resources they consume. This
//! upper bound is represented as [`sp_weights::Weight`].
//!
//! ## Future: PolkaVM
//!
//! With the transition of Pezkuwi relay chain to [JAM], a set of new features are being
//! introduced, one of which being a new virtual machine named [PolkaVM] that is as flexible as
//! WASM, but also capable of metering. This might alter the future of benchmarking in FRAME and
//! Pezkuwi-SDK, rendering them not needed anymore once PolkaVM is fully integrated into
//! Pezkuwi-sdk. For a basic explanation of JAM and PolkaVM, see [here](https://blog.kianenigma.com/posts/tech/demystifying-jam/#pvm).
//!
//!
//! [`frame-omni-bencher`]: https://crates.io/crates/frame-omni-bencher
//! [`templates`]: crate::pezkuwi_sdk::templates
//! [PolkaVM]: https://github.com/koute/polkavm
//! [JAM]: https://graypaper.com
#[frame::pallet(dev_mode)]
#[allow(unused_variables, unreachable_code, unused, clippy::diverging_sub_expression)]
pub mod pallet {
use frame::prelude::*;
#[docify::export]
pub trait WeightInfo {
fn simple_transfer() -> Weight;
}
#[pallet::config]
pub trait Config: frame_system::Config {
type WeightInfo: WeightInfo;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::call]
impl<T: Config> Pallet<T> {
#[docify::export]
#[pallet::weight(10_000)]
pub fn simple_transfer(
origin: OriginFor<T>,
destination: T::AccountId,
amount: u32,
) -> DispatchResult {
let destination_exists = todo!();
if destination_exists {
// simpler code path
} else {
// more complex code path
}
Ok(())
}
#[docify::export]
#[pallet::weight(T::WeightInfo::simple_transfer())]
pub fn simple_transfer_2(
origin: OriginFor<T>,
destination: T::AccountId,
amount: u32,
) -> DispatchResult {
let destination_exists = todo!();
if destination_exists {
// simpler code path
} else {
// more complex code path
}
Ok(())
}
#[docify::export]
// This is the worst-case, pre-dispatch weight.
#[pallet::weight(T::WeightInfo::simple_transfer())]
pub fn simple_transfer_3(
origin: OriginFor<T>,
destination: T::AccountId,
amount: u32,
) -> DispatchResultWithPostInfo {
// ^^ Notice the new return type
let destination_exists = todo!();
if destination_exists {
// simpler code path
// Note that need for .into(), to convert `()` to `PostDispatchInfo`
// See: https://docs.pezkuwichain.io/sdk/master/frame_support/dispatch/struct.PostDispatchInfo.html#impl-From%3C()%3E-for-PostDispatchInfo
Ok(().into())
} else {
// more complex code path
let actual_weight =
todo!("this can likely come from another benchmark that is NOT the worst case");
let pays_fee = todo!("You can set this to `Pays::Yes` or `Pays::No` to change if this transaction should pay fees");
Ok(frame::deps::frame_support::dispatch::PostDispatchInfo {
actual_weight: Some(actual_weight),
pays_fee,
})
}
}
}
}
@@ -0,0 +1,155 @@
//! # FRAME Logging
//!
//! This reference docs briefly explores how to do logging and printing runtimes, mainly
//! FRAME-based.
//!
//! > Please make sure to read [the section below](#using-logging-in-production) on using logging in
//! > production.
//!
//! ## Using `println!`
//!
//! To recap, as with standard Rust, you can use `println!` _in your tests_, but it will only print
//! out if executed with `--nocapture`, or if the test panics.
//!
//! ```
//! fn it_print() {
//! println!("Hello, world!");
//! }
//! ```
//!
//! within the pallet, if you want to use the standard `println!`, it needs to be wrapped in
//! [`sp_std::if_std`]. Of course, this means that this print code is only available to you in the
//! `std` compiler flag, and never present in a wasm build.
//!
//! ```
//! // somewhere in your pallet. This is not a real pallet code.
//! mod pallet {
//! struct Pallet;
//! impl Pallet {
//! fn print() {
//! sp_std::if_std! {
//! println!("Hello, world!");
//! }
//! }
//! }
//! }
//! ```
//!
//! ## Using `log`
//!
//! First, ensure you are familiar with the [`log`] crate. In short, each log statement has:
//!
//! 1. `log-level`, signifying how important it is.
//! 2. `log-target`, signifying to which component it belongs.
//!
//! Add log statements to your pallet as such:
//!
//! You can add the log crate to the `Cargo.toml` of the pallet.
//!
//! ```text
//! #[dependencies]
//! log = { version = "x.y.z", default-features = false }
//!
//! #[features]
//! std = [
//! // snip -- other pallets
//! "log/std"
//! ]
//! ```
//!
//! More conveniently, the `frame` umbrella crate re-exports the log crate as [`frame::log`].
//!
//! Then, the pallet can use this crate to emit log statements. In this statement, we use the info
//! level, and the target is `pallet-example`.
//!
//! ```
//! mod pallet {
//! struct Pallet;
//!
//! impl Pallet {
//! fn logs() {
//! frame::log::info!(target: "pallet-example", "Hello, world!");
//! }
//! }
//! }
//! ```
//!
//! This will in itself just emit the log messages, **but unless if captured by a logger, they will
//! not go anywhere**. [`sp_api`] provides a handy function to enable the runtime logging:
//!
//! ```
//! // in your test
//! fn it_also_prints() {
//! sp_api::init_runtime_logger();
//! // call into your pallet, and now it will print `log` statements.
//! }
//! ```
//!
//! Alternatively, you can use [`sp_tracing::try_init_simple`].
//!
//! `info`, `error` and `warn` logs are printed by default, but if you want lower level logs to also
//! be printed, you must to add the following compiler flag:
//!
//! ```text
//! RUST_LOG=pallet-example=trace cargo test
//! ```
//!
//! ## Enabling Logs in Production
//!
//! All logs from the runtime are emitted by default, but there is a feature flag in [`sp_api`],
//! called `disable-logging`, that can be used to disable all logs in the runtime. This is useful
//! for production chains to reduce the size and overhead of the wasm runtime.
#![doc = docify::embed!("../../substrate/primitives/api/src/lib.rs", init_runtime_logger)]
//!
//! Similar to the above, the proper `RUST_LOG` must also be passed to your compiler flag when
//! compiling the runtime.
//!
//! ## Log Target Prefixing
//!
//! Many [`crate::pezkuwi_sdk::frame_runtime`] pallets emit logs with log target `runtime::<name of
//! pallet>`, for example `runtime::system`. This then allows one to run a node with a wasm blob
//! compiled with `LOG_TARGET=runtime=debug`, which enables the log target of all pallets who's log
//! target starts with `runtime`.
//!
//! ## Low Level Primitives
//!
//! Under the hood, logging is another instance of host functions under the hood (as defined in
//! [`crate::reference_docs::wasm_meta_protocol`]). The runtime uses a set of host functions under
//! [`sp_io::logging`] and [`sp_io::misc`] to emit all logs and prints. You typically do not need to
//! use these APIs directly.
//!
//! ## Using Logging in Production
//!
//! Note that within FRAME, reading storage values __only for the purpose of logging__ is dangerous,
//! and can lead to consensus issues. This is because with the introduction of
//! [`crate::guides::enable_pov_reclaim`], the node side code will track the storage changes, and
//! tries to update the onchain record of the `proof_size` weight used (stored in
//! [`frame_system::BlockWeight`]) after the block is executed.
//!
//! If one node has a different log level enabled than the rest of the network, and the extra logs
//! impose additional storage reads, then the amount of `proof_size` weight reclaimed into
//! [`frame_system::BlockWeight`] will be different, causing a state root mismatch, which is
//! typically a fatal error emitted from [`frame_executive`].
//!
//! This also can also happen in a teyrchain context, and cause discrepancies between the relay
//! chain and the teyrchain, when execution the Teyrchain Validation Function (PVF) on the relay
//! chain.
//!
//! **In summary, you should only used storage values in logging (especially for levels lower than
//! `info` which is typically enabled by all parties) that are already read from storage, and will
//! be part of the storage proof of execution in any case**.
//!
//! A typical faulty code would look like this:
//!
//! ```ignore
//! /// This function will have a different storage footprint depending on the log level
//! fn faulty_logging() {
//! log::debug!(
//! "what I am about to print is only read when `RUST_LOG=debug` {:?}",
//! StorageValue::<T>::get()
//! );
//! }
//! ```
//!
//! Please read [this issue](https://github.com/pezkuwichain/pezkuwi-sdk/issues/155) for one
//! instance of the consensus issues caused by this mistake.
@@ -0,0 +1,114 @@
//! # Offchain Workers
//!
//! This reference document explains how offchain workers work in Substrate and FRAME. The main
//! focus is upon FRAME's implementation of this functionality. Nonetheless, offchain workers are a
//! Substrate-provided feature and can be used with possible alternatives to [`frame`] as well.
//!
//! Offchain workers are a commonly misunderstood topic, therefore we explain them bottom-up,
//! starting at the fundamentals and then describing the developer interface.
//!
//! ## Context
//!
//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that the node and the runtime
//! communicate with one another via host functions and runtime APIs. Many of these interactions
//! contribute to the actual state transition of the blockchain. For example [`sp_api::Core`] is the
//! main runtime API that is called to execute new blocks.
//!
//! Offchain workers are in principle not different in any way: It is a runtime API exposed by the
//! wasm blob ([`sp_offchain::OffchainWorkerApi`]), and the node software calls into it when it
//! deems fit. But, crucially, this API call is different in that:
//!
//! 1. It can have no impact on the state ie. it is _OFF (the) CHAIN_. If any state is altered
//! during the execution of this API call, it is discarded.
//! 2. It has access to an extended set of host functions that allow the wasm blob to do more. For
//! example, call into HTTP requests.
//!
//! > The main way through which an offchain worker can interact with the state is by submitting an
//! > extrinsic to the chain. This is the ONLY way to alter the state from an offchain worker.
//! > [`pallet_example_offchain_worker`] provides an example of this.
//!
//!
//! Given the "Off Chain" nature of this API, it is important to remember that calling this API is
//! entirely optional. Some nodes might call into it, some might not, and it would have no impact on
//! the execution of your blockchain because no state is altered no matter the execution of the
//! offchain worker API.
//!
//! Substrate's CLI allows some degree of configuration about this, allowing node operators to
//! specify when they want to run the offchain worker API. See
//! [`sc_cli::RunCmd::offchain_worker_params`].
//!
//! ## Nondeterministic Execution
//!
//! Needless to say, given the above description, the code in your offchain worker API can be
//! nondeterministic, as it is not part of the blockchain's STF, so it can be executed at unknown
//! times, by unknown nodes, and has no impact on the state. This is why an HTTP
//! ([`sp_runtime::offchain::http`]) API is readily provided to the offchain worker APIs. Because
//! there is no need for determinism in this context.
//!
//! > A common mistake here is for novice developers to see this HTTP API, and imagine that
//! > `pezkuwi-sdk` somehow magically solved the determinism in blockchains, and now a blockchain
//! > can make HTTP calls and it will all work. This is absolutely NOT the case. An HTTP call made
//! > by the offchain worker is non-deterministic by design. Blockchains can't and always won't be
//! > able to perform non-deterministic operations such as making HTTP calls to a foreign server.
//!
//! ## FRAME's API
//!
//! [`frame`] provides a simple API through which pallets can define offchain worker functions. This
//! is part of [`frame::traits::Hooks`], which is implemented as a part of
//! [`frame::pallet_macros::hooks`].
//!
//! ```
//! #[frame::pallet]
//! pub mod pallet {
//! use frame::prelude::*;
//!
//! #[pallet::config]
//! pub trait Config: frame_system::Config {}
//!
//! #[pallet::pallet]
//! pub struct Pallet<T>(_);
//!
//! #[pallet::hooks]
//! impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
//! fn offchain_worker(block_number: BlockNumberFor<T>) {
//! // ...
//! }
//! }
//! }
//! ```
//!
//! Additionally, [`sp_runtime::offchain`] provides a set of utilities that can be used to moderate
//! the execution of offchain workers.
//!
//! ## Think Twice: Why Use Substrate's Offchain Workers?
//!
//! Consider the fact that in principle, an offchain worker code written using the above API is no
//! different than an equivalent written with an _actual offchain interaction library_, such as
//! [Pezkuwi-JS](https://polkadot.js.org/docs/), or any of the other ones listed [here](https://github.com/substrate-developer-hub/awesome-substrate?tab=readme-ov-file#client-libraries).
//!
//! They can both read from the state, and have no means of updating the state, other than the route
//! of submitting an extrinsic to the chain. Therefore, it is worth thinking twice before embedding
//! a logic as a part of Substrate's offchain worker API. Does it have to be there? Can it not be a
//! simple, actual offchain application that lives outside of the chain's WASM blob?
//!
//! Some of the reasons why you might want to do the opposite, and actually embed an offchain worker
//! API into the WASM blob are:
//!
//! * Accessing the state is easier within the `offchain_worker` function, as it is already a part
//! of the runtime, and [`frame::pallet_macros::storage`] provides all the tools needed to read
//! the state. Other client libraries might provide varying degrees of capability here.
//! * It will be updated in synchrony with the runtime. A Substrate's offchain application is part
//! of the same WASM blob, and is therefore guaranteed to be up to date.
//!
//! For example, imagine you have modified a storage item to have a new type. This will possibly
//! require a [`crate::reference_docs::frame_runtime_upgrades_and_migrations`], and any offchain
//! code, such as a Pezkuwi-JS application, will have to be updated to reflect this change. Whereas
//! the WASM offchain worker code is guaranteed to already be updated, or else the runtime code will
//! not even compile.
//!
//!
//! ## Further References
//!
//! - <https://forum.polkadot.network/t/offchain-workers-design-assumptions-vulnerabilities/2548>
//! - <https://exchange.pezkuwichain.app/questions/11058/how-can-i-create-ocw-that-wont-activates-every-block-but-will-activates-only-w/11060#11060>
//! - [Offchain worker example](https://github.com/pezkuwichain/pezkuwi-sdk/tree/master/substrate/frame/examples/offchain-worker)
@@ -0,0 +1,270 @@
//! # FRAME Origin
//!
//! Let's start by clarifying a common wrong assumption about Origin:
//!
//! **ORIGIN IS NOT AN ACCOUNT ID**.
//!
//! FRAME's origin abstractions allow you to convey meanings far beyond just an account-id being the
//! caller of an extrinsic. Nonetheless, an account-id having signed an extrinsic is one of the
//! meanings that an origin can convey. This is the commonly used [`frame_system::ensure_signed`],
//! where the return value happens to be an account-id.
//!
//! Instead, let's establish the following as the correct definition of an origin:
//!
//! > The origin type represents the privilege level of the caller of an extrinsic.
//!
//! That is, an extrinsic, through checking the origin, can *express what privilege level it wishes
//! to impose on the caller of the extrinsic*. One of those checks can be as simple as "*any account
//! that has signed a statement can pass*".
//!
//! But the origin system can also express more abstract and complicated privilege levels. For
//! example:
//!
//! * If the majority of token holders agreed upon this. This is more or less what the
//! [`pallet_democracy`] does under the hood ([reference](https://github.com/pezkuwichain/pezkuwi-sdk/blob/edd95b3749754d2ed0c5738588e872c87be91624/substrate/frame/democracy/src/lib.rs#L1603-L1633)).
//! * If a specific ratio of an instance of [`pallet_collective`]/DAO agrees upon this.
//! * If another consensus system, for example a bridged network or a teyrchain, agrees upon this.
//! * If the majority of validator/authority set agrees upon this[^1].
//! * If caller holds a particular NFT.
//!
//! and many more.
//!
//! ## Context
//!
//! First, let's look at where the `origin` type is encountered in a typical pallet. The `origin:
//! OriginFor<T>` has to be the first argument of any given callable extrinsic in FRAME:
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", call_simple)]
//!
//! Typically, the code of an extrinsic starts with an origin check, such as
//! [`frame_system::ensure_signed`].
//!
//! Note that [`OriginFor`](frame_system::pallet_prelude::OriginFor) is merely a shorthand for
//! [`frame_system::Config::RuntimeOrigin`]. Given the name prefix `Runtime`, we can learn that
//! `RuntimeOrigin` is similar to `RuntimeCall` and others, a runtime composite enum that is
//! amalgamated at the runtime level. Read [`crate::reference_docs::frame_runtime_types`] to
//! familiarize yourself with these types.
//!
//! To understand this better, we will next create a pallet with a custom origin, which will add a
//! new variant to `RuntimeOrigin`.
//!
//! ## Adding Custom Pallet Origin to the Runtime
//!
//! For example, given a pallet that defines the following custom origin:
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin)]
//!
//! And a runtime with the following pallets:
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", runtime_exp)]
//!
//! The type [`crate::reference_docs::frame_origin::runtime_for_origin::RuntimeOrigin`] is expanded.
//! This `RuntimeOrigin` contains a variant for the [`frame_system::RawOrigin`] and the custom
//! origin of the pallet.
//!
//! > Notice how the [`frame_system::ensure_signed`] is nothing more than a `match` statement. If
//! > you want to know where the actual origin of an extrinsic is set (and the signature
//! > verification happens, if any), see
//! > [`sp_runtime::generic::CheckedExtrinsic#trait-implementations`], specifically
//! > [`sp_runtime::traits::Applyable`]'s implementation.
//!
//! ## Asserting on a Custom Internal Origin
//!
//! In order to assert on a custom origin that is defined within your pallet, we need a way to first
//! convert the `<T as frame_system::Config>::RuntimeOrigin` into the local `enum Origin` of the
//! current pallet. This is a common process that is explained in
//! [`crate::reference_docs::frame_runtime_types#
//! adding-further-constraints-to-runtime-composite-enums`].
//!
//! We use the same process here to express that `RuntimeOrigin` has a number of additional bounds,
//! as follows.
//!
//! 1. Defining a custom `RuntimeOrigin` with further bounds in the pallet.
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_bound)]
//!
//! 2. Using it in the pallet.
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_usage)]
//!
//! ## Asserting on a Custom External Origin
//!
//! Very often, a pallet wants to have a parameterized origin that is **NOT** defined within the
//! pallet. In other words, a pallet wants to delegate an origin check to something that is
//! specified later at the runtime level. Like many other parameterizations in FRAME, this implies
//! adding a new associated type to `trait Config`.
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_def)]
//!
//! Then, within the pallet, we can simply use this "unknown" origin check type:
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_usage)]
//!
//! Finally, at the runtime, any implementation of [`frame::traits::EnsureOrigin`] can be passed.
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_provide)]
//!
//! Indeed, some of these implementations of [`frame::traits::EnsureOrigin`] are similar to the ones
//! that we know about: [`frame::runtime::prelude::EnsureSigned`],
//! [`frame::runtime::prelude::EnsureSignedBy`], [`frame::runtime::prelude::EnsureRoot`],
//! [`frame::runtime::prelude::EnsureNone`], etc. But, there are also many more that are not known
//! to us, and are defined in other pallets.
//!
//! For example, [`pallet_collective`] defines [`pallet_collective::EnsureMember`] and
//! [`pallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we alluded
//! to earlier in this document.
//!
//! Make sure to check the full list of [implementors of
//! `EnsureOrigin`](frame::traits::EnsureOrigin#implementors) for more inspiration.
//!
//! ## Obtaining Abstract Origins
//!
//! So far we have learned that FRAME pallets can assert on custom and abstract origin types,
//! whether they are defined within the pallet or not. But how can we obtain these abstract origins?
//!
//! > All extrinsics that come from the outer world can generally only be obtained as either
//! > `signed` or `none` origin.
//!
//! Generally, these abstract origins are only obtained within the runtime, when a call is
//! dispatched within the runtime.
//!
//! ## Further References
//!
//! - [Gavin Wood's speech about FRAME features at Protocol Berg 2023.](https://youtu.be/j7b8Upipmeg?si=83_XUgYuJxMwWX4g&t=195)
//! - [A related StackExchange question.](https://exchange.pezkuwichain.app/questions/10992/how-do-you-find-the-public-key-for-the-medium-spender-track-origin)
//!
//! [^1]: Inherents are essentially unsigned extrinsics that need an [`frame_system::ensure_none`]
//! origin check, and through the virtue of being an inherent, are agreed upon by all validators.
use frame::prelude::*;
#[frame::pallet(dev_mode)]
pub mod pallet_for_origin {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[docify::export(call_simple)]
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn do_something(_origin: OriginFor<T>) -> DispatchResult {
// ^^^^^^^^^^^^^^^^^^^^^
todo!();
}
}
}
#[frame::pallet(dev_mode)]
pub mod pallet_with_custom_origin {
use super::*;
#[docify::export(custom_origin_bound)]
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeOrigin: From<<Self as frame_system::Config>::RuntimeOrigin>
+ Into<Result<Origin, <Self as Config>::RuntimeOrigin>>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[docify::export(custom_origin)]
/// A dummy custom origin.
#[pallet::origin]
#[derive(
PartialEq,
Eq,
Clone,
RuntimeDebug,
Encode,
Decode,
DecodeWithMemTracking,
TypeInfo,
MaxEncodedLen,
)]
pub enum Origin {
/// If all holders of a particular NFT have agreed upon this.
AllNftHolders,
/// If all validators have agreed upon this.
ValidatorSet,
}
#[docify::export(custom_origin_usage)]
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn only_validators(origin: OriginFor<T>) -> DispatchResult {
// first, we convert from `<T as frame_system::Config>::RuntimeOrigin` to `<T as
// Config>::RuntimeOrigin`
let local_runtime_origin = <<T as Config>::RuntimeOrigin as From<
<T as frame_system::Config>::RuntimeOrigin,
>>::from(origin);
// then we convert to `origin`, if possible
let local_origin =
local_runtime_origin.into().map_err(|_| "invalid origin type provided")?;
ensure!(matches!(local_origin, Origin::ValidatorSet), "Not authorized");
todo!();
}
}
}
pub mod runtime_for_origin {
use super::pallet_with_custom_origin;
use frame::{runtime::prelude::*, testing_prelude::*};
#[docify::export(runtime_exp)]
construct_runtime!(
pub struct Runtime {
System: frame_system,
PalletWithCustomOrigin: pallet_with_custom_origin,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Self>;
}
impl pallet_with_custom_origin::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
}
}
#[frame::pallet(dev_mode)]
pub mod pallet_with_external_origin {
use super::*;
#[docify::export(external_origin_def)]
#[pallet::config]
pub trait Config: frame_system::Config {
type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[docify::export(external_origin_usage)]
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn externally_checked_ext(origin: OriginFor<T>) -> DispatchResult {
T::ExternalOrigin::ensure_origin(origin)?;
todo!();
}
}
}
pub mod runtime_for_external_origin {
use super::*;
use frame::{runtime::prelude::*, testing_prelude::*};
construct_runtime!(
pub struct Runtime {
System: frame_system,
PalletWithExternalOrigin: pallet_with_external_origin,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Self>;
}
#[docify::export(external_origin_provide)]
impl pallet_with_external_origin::Config for Runtime {
type ExternalOrigin = EnsureSigned<<Self as frame_system::Config>::AccountId>;
}
}
@@ -0,0 +1,296 @@
//! # FRAME Pallet Coupling
//!
//! This reference document explains how FRAME pallets can be combined to interact together.
//!
//! It is suggested to re-read [`crate::pezkuwi_sdk::frame_runtime`], notably the information
//! around [`frame::pallet_macros::config`]. Recall that:
//!
//! > Configuration trait of a pallet: It allows a pallet to receive types at a later
//! > point from the runtime that wishes to contain it. It allows the pallet to be parameterized
//! > over both types and values.
//!
//! ## Context, Background
//!
//! FRAME pallets, as per described in [`crate::pezkuwi_sdk::frame_runtime`] are:
//!
//! > A pallet is a unit of encapsulated logic. It has a clearly defined responsibility and can be
//! linked to other pallets.
//!
//! That is to say:
//!
//! * *encapsulated*: Ideally, a FRAME pallet contains encapsulated logic which has clear
//! boundaries. It is generally a bad idea to build a single monolithic pallet that does multiple
//! things, such as handling currencies, identities and staking all at the same time.
//! * *linked to other pallets*: But, adhering extensively to the above also hinders the ability to
//! write useful applications. Pallets often need to work with each other, communicate and use
//! each other's functionalities.
//!
//! The broad principle that allows pallets to be linked together is the same way through which a
//! pallet uses its `Config` trait to receive types and values from the runtime that contains it.
//!
//! There are generally two ways to achieve this:
//!
//! 1. Tight coupling pallets.
//! 2. Loose coupling pallets.
//!
//! To explain the difference between the two, consider two pallets, `A` and `B`. In both cases, `A`
//! wants to use some functionality exposed by `B`.
//!
//! When tightly coupling pallets, `A` can only exist in a runtime if `B` is also present in the
//! same runtime. That is, `A` is expressing that can only work if `B` is present.
//!
//! This translates to the following Rust code:
//!
//! ```
//! trait Pallet_B_Config {}
//! trait Pallet_A_Config: Pallet_B_Config {}
//! ```
//!
//! Contrary, when pallets are loosely coupled, `A` expresses that some functionality, expressed via
//! a trait `F`, needs to be fulfilled. This trait is then implemented by `B`, and the two pallets
//! are linked together at the runtime level. This means that `A` only relies on the implementation
//! of `F`, which may be `B`, or another implementation of `F`.
//!
//! This translates to the following Rust code:
//!
//! ```
//! trait F {}
//! trait Pallet_A_Config {
//! type F: F;
//! }
//! // Pallet_B will implement and fulfill `F`.
//! ```
//!
//! ## Example
//!
//! Consider the following example, in which `pallet-foo` needs another pallet to provide the block
//! author to it, and `pallet-author` which has access to this information.
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_foo)]
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_author)]
//!
//! ### Tight Coupling Pallets
//!
//! To tightly couple `pallet-foo` and `pallet-author`, we use Rust's supertrait system. When a
//! pallet makes its own `trait Config` be bounded by another pallet's `trait Config`, it is
//! expressing two things:
//!
//! 1. That it can only exist in a runtime if the other pallet is also present.
//! 2. That it can use the other pallet's functionality.
//!
//! `pallet-foo`'s `Config` would then look like:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_config)]
//!
//! And `pallet-foo` can use the method exposed by `pallet_author::Pallet` directly:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", tight_usage)]
//!
//!
//! ### Loosely Coupling Pallets
//!
//! If `pallet-foo` wants to *not* rely on `pallet-author` directly, it can leverage its
//! `Config`'s associated types. First, we need a trait to express the functionality that
//! `pallet-foo` wants to obtain:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", AuthorProvider)]
//!
//! > We sometimes refer to such traits that help two pallets interact as "glue traits".
//!
//! Next, `pallet-foo` states that it needs this trait to be provided to it, at the runtime level,
//! via an associated type:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_config)]
//!
//! Then, `pallet-foo` can use this trait to obtain the block author, without knowing where it comes
//! from:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_usage)]
//!
//! Then, if `pallet-author` implements this glue-trait:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pallet_author_provider)]
//!
//! And upon the creation of the runtime, the two pallets are linked together as such:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", runtime_author_provider)]
//!
//! Crucially, when using loose coupling, we gain the flexibility of providing different
//! implementations of `AuthorProvider`, such that different users of a `pallet-foo` can use
//! different ones, without any code change being needed. For example, in the code snippets of this
//! module, you can find [`OtherAuthorProvider`], which is an alternative implementation of
//! [`AuthorProvider`].
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", other_author_provider)]
//!
//! A common pattern in pezkuwi-sdk is to provide an implementation of such glu traits for the unit
//! type as a "default/test behavior".
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", unit_author_provider)]
//!
//! ## Frame System
//!
//! With the above information in context, we can conclude that **`frame_system` is a special pallet
//! that is tightly coupled with every other pallet**. This is because it provides the fundamental
//! system functionality that every pallet needs, such as some types like
//! [`frame::prelude::frame_system::Config::AccountId`],
//! [`frame::prelude::frame_system::Config::Hash`], and some functionality such as block number,
//! etc.
//!
//! ## Recap
//!
//! To recap, consider the following rules of thumb:
//!
//! * In all cases, try and break down big pallets apart with clear boundaries of responsibility. In
//! general, it is easier to argue about multiple pallet if they only communicate together via a
//! known trait, rather than having access to all of each others public items, such as storage and
//! dispatchables.
//! * If a group of pallets is meant to work together, but is not foreseen to be generalized, or
//! used by others, consider tightly coupling pallets, *if it simplifies the development*.
//! * If a pallet needs a functionality provided by another pallet, but multiple implementations can
//! be foreseen, consider loosely coupling pallets.
//!
//! For example, all pallets in `pezkuwi-sdk` that needed to work with currencies could have been
//! tightly coupled with [`pallet_balances`]. But, `pezkuwi-sdk` also provides [`pallet_assets`]
//! (and more implementations by the community), therefore all pallets use traits to loosely couple
//! with balances or assets pallet. More on this in [`crate::reference_docs::frame_tokens`].
//!
//! ## Further References
//!
//! - <https://www.youtube.com/watch?v=0eNGZpNkJk4>
//! - <https://exchange.pezkuwichain.app/questions/922/pallet-loose-couplingtight-coupling-and-missing-traits>
//!
//! [`AuthorProvider`]: crate::reference_docs::frame_pallet_coupling::AuthorProvider
//! [`OtherAuthorProvider`]: crate::reference_docs::frame_pallet_coupling::OtherAuthorProvider
#![allow(unused)]
use frame::prelude::*;
#[docify::export]
#[frame::pallet]
pub mod pallet_foo {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
impl<T: Config> Pallet<T> {
fn do_stuff_with_author() {
// needs block author here
}
}
}
#[docify::export]
#[frame::pallet]
pub mod pallet_author {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
impl<T: Config> Pallet<T> {
pub fn author() -> T::AccountId {
todo!("somehow has access to the block author and can return it here")
}
}
}
#[frame::pallet]
pub mod pallet_foo_tight {
use super::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[docify::export(tight_config)]
/// This pallet can only live in a runtime that has both `frame_system` and `pallet_author`.
#[pallet::config]
pub trait Config: frame_system::Config + pallet_author::Config {}
#[docify::export(tight_usage)]
impl<T: Config> Pallet<T> {
// anywhere in `pallet-foo`, we can call into `pallet-author` directly, namely because
// `T: pallet_author::Config`
fn do_stuff_with_author() {
let _ = pallet_author::Pallet::<T>::author();
}
}
}
#[docify::export]
/// Abstraction over "something that can provide the block author".
pub trait AuthorProvider<AccountId> {
fn author() -> AccountId;
}
#[frame::pallet]
pub mod pallet_foo_loose {
use super::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[docify::export(loose_config)]
#[pallet::config]
pub trait Config: frame_system::Config {
/// This pallet relies on the existence of something that implements [`AuthorProvider`],
/// which may or may not be `pallet-author`.
type AuthorProvider: AuthorProvider<Self::AccountId>;
}
#[docify::export(loose_usage)]
impl<T: Config> Pallet<T> {
fn do_stuff_with_author() {
let _ = T::AuthorProvider::author();
}
}
}
#[docify::export(pallet_author_provider)]
impl<T: pallet_author::Config> AuthorProvider<T::AccountId> for pallet_author::Pallet<T> {
fn author() -> T::AccountId {
pallet_author::Pallet::<T>::author()
}
}
pub struct OtherAuthorProvider;
#[docify::export(other_author_provider)]
impl<AccountId> AuthorProvider<AccountId> for OtherAuthorProvider {
fn author() -> AccountId {
todo!("somehow get the block author here")
}
}
#[docify::export(unit_author_provider)]
impl<AccountId> AuthorProvider<AccountId> for () {
fn author() -> AccountId {
todo!("somehow get the block author here")
}
}
pub mod runtime {
use super::*;
use cumulus_pallet_aura_ext::pallet;
use frame::{runtime::prelude::*, testing_prelude::*};
construct_runtime!(
pub struct Runtime {
System: frame_system,
PalletFoo: pallet_foo_loose,
PalletAuthor: pallet_author,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Self>;
}
impl pallet_author::Config for Runtime {}
#[docify::export(runtime_author_provider)]
impl pallet_foo_loose::Config for Runtime {
type AuthorProvider = pallet_author::Pallet<Runtime>;
// which is also equivalent to
// type AuthorProvider = PalletAuthor;
}
}
@@ -0,0 +1,320 @@
//! # FRAME Runtime Types
//!
//! This reference document briefly explores the idea around types generated at the runtime level by
//! the FRAME macros.
//!
//! > As of now, many of these important types are generated within the internals of
//! > [`construct_runtime`], and there is no easy way for you to visually know they exist.
//! > [#pezkuwi-sdk#1378](https://github.com/paritytech/polkadot-sdk/pull/1378) is meant to
//! > significantly improve this. Exploring the rust-docs of a runtime, such as [`runtime`] which is
//! > defined in this module is as of now the best way to learn about these types.
//!
//! ## Composite Enums
//!
//! Many types within a FRAME runtime follow the following structure:
//!
//! * Each individual pallet defines a type, for example `Foo`.
//! * At the runtime level, these types are amalgamated into a single type, for example
//! `RuntimeFoo`.
//!
//! As the names suggest, all composite enums in a FRAME runtime start their name with `Runtime`.
//! For example, `RuntimeCall` is a representation of the most high level `Call`-able type in the
//! runtime.
//!
//! Composite enums are generally convertible to their individual parts as such:
#![doc = simple_mermaid::mermaid!("../../../mermaid/outer_runtime_types.mmd")]
//!
//! In that one can always convert from the inner type into the outer type, but not vice versa. This
//! is usually expressed by implementing `From`, `TryFrom`, `From<Result<_>>` and similar traits.
//!
//! ### Example
//!
//! We provide the following two pallets: [`pallet_foo`] and [`pallet_bar`]. Each define a
//! dispatchable, and `Foo` also defines a custom origin. Lastly, `Bar` defines an additional
//! `GenesisConfig`.
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_foo)]
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_bar)]
//!
//! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and
//! [`RuntimeGenesisConfig`] generated in [`runtime`] respectively.
//!
//! As observed, [`RuntimeCall`] has 3 variants, one for each pallet and one for `frame_system`. If
//! you explore further, you will soon realize that each variant is merely a pointer to the `Call`
//! type in each pallet, for example [`pallet_foo::Call`].
//!
//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for `pallet_foo`
//! which utilized [`frame::pallet_macros::origin`].
//!
//! Finally, [`RuntimeGenesisConfig`] is composed of `frame_system` and a variant for `pallet_bar`'s
//! [`pallet_bar::GenesisConfig`].
//!
//! You can find other composite enums by scanning [`runtime`] for other types who's name starts
//! with `Runtime`. Some of the more noteworthy ones are:
//!
//! - [`RuntimeEvent`]
//! - [`RuntimeError`]
//! - [`RuntimeHoldReason`]
//!
//! ### Adding Further Constraints to Runtime Composite Enums
//!
//! This section explores a common scenario where a pallet has access to one of these runtime
//! composite enums, but it wishes to further specify it by adding more trait bounds to it.
//!
//! Let's take the example of `RuntimeCall`. This is an associated type in
//! [`frame_system::Config::RuntimeCall`], and all pallets have access to this type, because they
//! have access to [`frame_system::Config`]. Finally, this type is meant to be set to outer call of
//! the entire runtime.
//!
//! But, let's not forget that this is information that *we know*, and the Rust compiler does not.
//! All that the rust compiler knows about this type is *ONLY* what the trait bounds of
//! [`frame_system::Config::RuntimeCall`] are specifying:
#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", system_runtime_call)]
//!
//! So, when at a given pallet, one accesses `<T as frame_system::Config>::RuntimeCall`, the type is
//! extremely opaque from the perspective of the Rust compiler.
//!
//! How can a pallet access the `RuntimeCall` type with further constraints? For example, each
//! pallet has its own `enum Call`, and knows that its local `Call` is a part of `RuntimeCall`,
//! therefore there should be a `impl From<Call<_>> for RuntimeCall`.
//!
//! The only way to express this using Rust's associated types is for the pallet to **define its own
//! associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**.
//!
//! In this case, we will want to assert the existence of [`frame::traits::IsSubType`], which is
//! very similar to [`TryFrom`].
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call)]
//!
//! And indeed, at the runtime level, this associated type would be the same `RuntimeCall` that is
//! passed to `frame_system`.
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_with_specific_runtime_call_impl)]
//!
//! > In other words, the degree of specificity that [`frame_system::Config::RuntimeCall`] has is
//! > not enough for the pallet to work with. Therefore, the pallet has to define its own associated
//! > type representing `RuntimeCall`.
//!
//! Another way to look at this is:
//!
//! `pallet_with_specific_runtime_call::Config::RuntimeCall` and `frame_system::Config::RuntimeCall`
//! are two different representations of the same concrete type that is only known when the runtime
//! is being constructed.
//!
//! Now, within this pallet, this new `RuntimeCall` can be used, and it can use its new trait
//! bounds, such as being [`frame::traits::IsSubType`]:
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call_usages)]
//!
//! > Once Rust's "_Associated Type Bounds RFC_" is usable, this syntax can be used to
//! > simplify the above scenario. See [this](https://github.com/pezkuwichain/pezkuwi-sdk/issues/133)
//! > issue for more information.
//!
//! ### Asserting Equality of Multiple Runtime Composite Enums
//!
//! Recall that in the above example, `<T as Config>::RuntimeCall` and `<T as
//! frame_system::Config>::RuntimeCall` are expected to be equal types, but at the compile-time we
//! have to represent them with two different associated types with different bounds. Would it not
//! be cool if we had a test to make sure they actually resolve to the same concrete type once the
//! runtime is constructed? The following snippet exactly does that:
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)]
//!
//! We leave it to the reader to further explore what [`frame::traits::Hooks::integrity_test`] is,
//! and what [`core::any::TypeId`] is. Another way to assert this is using
//! [`frame::traits::IsType`].
//!
//! ## Type Aliases
//!
//! A number of type aliases are generated by the `construct_runtime` which are also noteworthy:
//!
//! * [`runtime::PalletFoo`] is an alias to [`pallet_foo::Pallet`]. Same for `PalletBar`, and
//! `System`
//! * [`runtime::AllPalletsWithSystem`] is an alias for a tuple of all of the above. This type is
//! important to FRAME internals such as `executive`, as it implements traits such as
//! [`frame::traits::Hooks`].
//!
//! ## Further Details
//!
//! * [`crate::reference_docs::frame_origin`] explores further details about the usage of
//! `RuntimeOrigin`.
//! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an
//! extrinsic. See [`crate::reference_docs::transaction_extensions`] for more information.
//! * See the documentation of [`construct_runtime`].
//! * See the corresponding lecture in the [PBA Lectures](https://www.youtube.com/watch?v=OCBC1pMYPoc&list=PL-w_i5kwVqbni1Ch2j_RwTIXiB-bwnYqq&index=11).
//!
//!
//! [`construct_runtime`]: frame::runtime::prelude::construct_runtime
//! [`runtime::PalletFoo`]: crate::reference_docs::frame_runtime_types::runtime::PalletFoo
//! [`runtime::AllPalletsWithSystem`]: crate::reference_docs::frame_runtime_types::runtime::AllPalletsWithSystem
//! [`runtime`]: crate::reference_docs::frame_runtime_types::runtime
//! [`pallet_foo`]: crate::reference_docs::frame_runtime_types::pallet_foo
//! [`pallet_foo::Call`]: crate::reference_docs::frame_runtime_types::pallet_foo::Call
//! [`pallet_foo::Pallet`]: crate::reference_docs::frame_runtime_types::pallet_foo::Pallet
//! [`pallet_bar`]: crate::reference_docs::frame_runtime_types::pallet_bar
//! [`pallet_bar::GenesisConfig`]: crate::reference_docs::frame_runtime_types::pallet_bar::GenesisConfig
//! [`RuntimeEvent`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeEvent
//! [`RuntimeGenesisConfig`]:
//! crate::reference_docs::frame_runtime_types::runtime::RuntimeGenesisConfig
//! [`RuntimeOrigin`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeOrigin
//! [`OriginCaller`]: crate::reference_docs::frame_runtime_types::runtime::OriginCaller
//! [`RuntimeError`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeError
//! [`RuntimeCall`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeCall
//! [`RuntimeHoldReason`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeHoldReason
use frame::prelude::*;
#[docify::export]
#[frame::pallet(dev_mode)]
pub mod pallet_foo {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::origin]
#[derive(
PartialEq,
Eq,
Clone,
RuntimeDebug,
Encode,
Decode,
DecodeWithMemTracking,
TypeInfo,
MaxEncodedLen,
)]
pub enum Origin {
A,
B,
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
todo!();
}
pub fn other(_origin: OriginFor<T>) -> DispatchResult {
todo!();
}
}
}
#[docify::export]
#[frame::pallet(dev_mode)]
pub mod pallet_bar {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::genesis_config]
#[derive(DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
pub initial_account: Option<T::AccountId>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn bar(_origin: OriginFor<T>) -> DispatchResult {
todo!();
}
}
}
pub mod runtime {
use super::{pallet_bar, pallet_foo};
use frame::{runtime::prelude::*, testing_prelude::*};
#[docify::export(runtime_exp)]
construct_runtime!(
pub struct Runtime {
System: frame_system,
PalletFoo: pallet_foo,
PalletBar: pallet_bar,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Self>;
}
impl pallet_foo::Config for Runtime {}
impl pallet_bar::Config for Runtime {}
}
#[frame::pallet(dev_mode)]
pub mod pallet_with_specific_runtime_call {
use super::*;
use frame::traits::IsSubType;
#[docify::export(custom_runtime_call)]
/// A pallet that wants to further narrow down what `RuntimeCall` is.
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeCall: IsSubType<Call<Self>>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
// note that this pallet needs some `call` to have a `enum Call`.
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
todo!();
}
}
#[docify::export(custom_runtime_call_usages)]
impl<T: Config> Pallet<T> {
fn _do_something_useful_with_runtime_call(call: <T as Config>::RuntimeCall) {
// check if the runtime call given is of this pallet's variant.
let _maybe_my_call: Option<&Call<T>> = call.is_sub_type();
todo!();
}
}
#[docify::export(assert_equality)]
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn integrity_test() {
use core::any::TypeId;
assert_eq!(
TypeId::of::<<T as Config>::RuntimeCall>(),
TypeId::of::<<T as frame_system::Config>::RuntimeCall>()
);
}
}
}
pub mod runtime_with_specific_runtime_call {
use super::pallet_with_specific_runtime_call;
use frame::{runtime::prelude::*, testing_prelude::*};
construct_runtime!(
pub struct Runtime {
System: frame_system,
PalletWithSpecificRuntimeCall: pallet_with_specific_runtime_call,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = MockBlock<Self>;
}
#[docify::export(pallet_with_specific_runtime_call_impl)]
impl pallet_with_specific_runtime_call::Config for Runtime {
// an implementation of `IsSubType` is provided by `construct_runtime`.
type RuntimeCall = RuntimeCall;
}
}
@@ -0,0 +1,134 @@
//! # Runtime Upgrades
//!
//! At their core, blockchain logic consists of
//!
//! 1. on-chain state,
//! 2. a state transition function.
//!
//! In Substrate-based blockchains, state transition functions are referred to as
//! [runtimes](https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/blockchain_state_machines/index.html).
//!
//! Traditionally, before Substrate, upgrading state transition functions required node
//! operators to download new software and restart their nodes in a process called
//! [forking](https://en.wikipedia.org/wiki/Fork_(blockchain)).
//!
//! Substrate-based blockchains do not require forking, and instead upgrade runtimes
//! in a process called "Runtime Upgrades".
//!
//! Forkless runtime upgrades are a defining feature of the Substrate framework. Updating the
//! runtime logic without forking the code base enables your blockchain to seamlessly evolve
//! over time in a deterministic, rules-based manner. It also removes ambiguity for node operators
//! and other participants in the network about what is the canonical runtime.
//!
//! This capability is possible due to the runtime of a blockchain existing in on-chain storage.
//!
//! ## Performing a Runtime Upgrade
//!
//! To upgrade a runtime, an [`Origin`](frame_system::RawOrigin) with the necessary permissions
//! (usually via governance) changes the `:code` storage. Usually, this is performed via a call to
//! [`set_code`] (or [`set_code_without_checks`]) with the desired new runtime blob, scheduled
//! using [`pallet_scheduler`].
//!
//! Prior to building the new runtime, don't forget to update the
//! [`RuntimeVersion`](sp_version::RuntimeVersion).
//!
//! # Migrations
//!
//! It is often desirable to define logic to execute immediately after runtime upgrades (see
//! [this diagram](frame::traits::Hooks)).
//!
//! Self-contained pieces of logic that execute after a runtime upgrade are called "Migrations".
//!
//! The typical use case of a migration is to 'migrate' pallet storage from one layout to another,
//! for example when the encoding of a storage item is changed. However, they can also execute
//! arbitrary logic such as:
//!
//! - Calling arbitrary pallet methods.
//! - Mutating arbitrary on-chain state.
//! - Cleaning up some old storage items that are no longer needed.
//!
//! ## Single Block Migrations
//!
//! - Execute immediately and entirely at the beginning of the block following
//! a runtime upgrade.
//! - Are suitable for migrations which are guaranteed to not exceed the block weight.
//! - Are simply implementations of [`OnRuntimeUpgrade`].
//!
//! To learn best practices for writing single block pallet storage migrations, see the
//! [Single Block Migration Example Pallet](pallet_example_single_block_migrations).
//!
//! ### Scheduling the Single Block Migrations to Run Next Runtime Upgrade
//!
//! Schedule migrations to run next runtime upgrade passing them as a parameter to your
//! [`Config`](frame_system) pallet:
//!
//! ```ignore
//! /// Tuple of migrations (structs that implement `OnRuntimeUpgrade`)
//! type Migrations = (
//! pallet_example_storage_migration::migrations::v1::versioned::MigrateV0ToV1,
//! MyCustomMigration,
//! // ...more migrations here
//! );
//! impl frame_system::Config for Runtime {
//! type SingleBlockMigrations = Migrations;
//! }
//! ```
//!
//! ### Ensuring Single Block Migration Safety
//!
//! "My migration unit tests pass, so it should be safe to deploy right?"
//!
//! No! Unit tests execute the migration in a very simple test environment, and cannot account
//! for the complexities of a real runtime or real on-chain state.
//!
//! Prior to deploying migrations, it is critical to perform additional checks to ensure that when
//! run in our real runtime they will not brick the chain due to:
//! - Panicking.
//! - Touching too many storage keys and resulting in an excessively large PoV.
//! - Taking too long to execute.
//!
//! [`try-runtime-cli`](https://github.com/paritytech/try-runtime-cli) has a sub-command
//! [`on-runtime-upgrade`](https://paritytech.github.io/try-runtime-cli/try_runtime_core/commands/enum.Action.html#variant.OnRuntimeUpgrade)
//! which is designed to help with exactly this.
//!
//! Developers MUST run this command before deploying migrations to ensure they will not
//! inadvertently result in a bricked chain.
//!
//! It is recommended to run as part of your CI pipeline. See the
//! [pezkuwi-sdk check-runtime-migration job](https://github.com/pezkuwichain/pezkuwi-sdk/blob/4a293bc5a25be637c06ce950a34490706597615b/.gitlab/pipeline/check.yml#L103-L124)
//! for an example of how to configure this.
//!
//! ### Note on the Manipulability of PoV Size and Execution Time
//!
//! While [`try-runtime-cli`](https://github.com/paritytech/try-runtime-cli) can help ensure with
//! very high certainty that a migration will succeed given **existing** on-chain state, it cannot
//! prevent a malicious actor from manipulating state in a way that will cause the migration to take
//! longer or produce a PoV much larger than previously measured.
//!
//! Therefore, it is important to write migrations in such a way that the execution time or PoV size
//! it adds to the block cannot be easily manipulated. e.g., do not iterate over storage that can
//! quickly or cheaply be bloated.
//!
//! If writing your migration in such a way is not possible, a multi block migration should be used
//! instead.
//!
//! ### Other useful tools
//!
//! [`Chopsticks`](https://github.com/AcalaNetwork/chopsticks) is another tool in the Substrate
//! ecosystem which developers may find useful to use in addition to `try-runtime-cli` when testing
//! their single block migrations.
//!
//! ## Multi Block Migrations
//!
//! Safely and easily execute long-running migrations across multiple blocks.
//!
//! Suitable for migrations which could use arbitrary amounts of block weight.
//!
//! See the
//! [multi-block-migrations example](https://github.com/pezkuwichain/pezkuwi-sdk/tree/0d7d2177807ec6b3094f4491a45b0bc0d74d3c8b/substrate/frame/examples/multi-block-migrations)
//! for reference.
//!
//! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade
//! [`StorageVersion`]: frame_support::traits::StorageVersion
//! [`set_code`]: frame_system::Call::set_code
//! [`set_code_without_checks`]: frame_system::Call::set_code_without_checks
@@ -0,0 +1,202 @@
//! # Frame storage derives
//!
//! > **Note:**
//! >
//! > In all examples, a few lines of boilerplate have been hidden from each snippet for
//! > conciseness.
//!
//! Let's begin by starting to store a `NewType` in a storage item:
//!
//! ```compile_fail
//! #[frame::pallet]
//! pub mod pallet {
//! # use frame::prelude::*;
//! # #[pallet::config]
//! # pub trait Config: frame_system::Config {}
//! # #[pallet::pallet]
//! # pub struct Pallet<T>(_);
//! pub struct NewType(u32);
//
//! #[pallet::storage]
//! pub type Something<T> = StorageValue<_, NewType>;
//! }
//! ```
//!
//! This raises a number of compiler errors, like:
//! ```text
//! the trait `MaxEncodedLen` is not implemented for `NewType`, which is required by
//! `frame::prelude::StorageValue<_GeneratedPrefixForStorageSomething<T>, NewType>:
//! StorageInfoTrait`
//! ```
//!
//! This implies the following set of traits that need to be derived for a type to be stored in
//! `frame` storage:
//! ```rust
//! #[frame::pallet]
//! pub mod pallet {
//! # use frame::prelude::*;
//! # #[pallet::config]
//! # pub trait Config: frame_system::Config {}
//! # #[pallet::pallet]
//! # pub struct Pallet<T>(_);
//! #[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
//! pub struct NewType(u32);
//!
//! #[pallet::storage]
//! pub type Something<T> = StorageValue<_, NewType>;
//! }
//! ```
//!
//! Next, let's look at how this will differ if we are to store a type that is derived from `T` in
//! storage, such as [`frame::prelude::BlockNumberFor`]:
//! ```compile_fail
//! #[frame::pallet]
//! pub mod pallet {
//! # use frame::prelude::*;
//! # #[pallet::config]
//! # pub trait Config: frame_system::Config {}
//! # #[pallet::pallet]
//! # pub struct Pallet<T>(_);
//! #[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
//! pub struct NewType<T: Config>(BlockNumberFor<T>);
//!
//! #[pallet::storage]
//! pub type Something<T: Config> = StorageValue<_, NewType<T>>;
//! }
//! ```
//!
//! Surprisingly, this will also raise a number of errors, like:
//! ```text
//! the trait `TypeInfo` is not implemented for `T`, which is required
//! by`frame_support::pallet_prelude::StorageValue<pallet_2::_GeneratedPrefixForStorageSomething<T>,
//! pallet_2::NewType<T>>:StorageEntryMetadataBuilder
//! ```
//!
//! Why is that? The underlying reason is that the `TypeInfo` `derive` macro will only work for
//! `NewType` if all of `NewType`'s generics also implement `TypeInfo`. This is not the case for `T`
//! in the example above.
//!
//! If you expand an instance of the derive, you will find something along the lines of:
//! `impl<T> TypeInfo for NewType<T> where T: TypeInfo { ... }`. This is the reason why the
//! `TypeInfo` trait is required for `T`.
//!
//! To fix this, we need to add a `#[scale_info(skip_type_params(T))]`
//! attribute to `NewType`. This additional macro will instruct the `derive` to skip the bound on
//! `T`.
//! ```rust
//! #[frame::pallet]
//! pub mod pallet {
//! # use frame::prelude::*;
//! # #[pallet::config]
//! # pub trait Config: frame_system::Config {}
//! # #[pallet::pallet]
//! # pub struct Pallet<T>(_);
//! #[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo)]
//! #[scale_info(skip_type_params(T))]
//! pub struct NewType<T: Config>(BlockNumberFor<T>);
//!
//! #[pallet::storage]
//! pub type Something<T: Config> = StorageValue<_, NewType<T>>;
//! }
//! ```
//!
//! Next, let's say we wish to store `NewType` as [`frame::prelude::ValueQuery`], which means it
//! must also implement `Default`. This should be as simple as adding `derive(Default)` to it,
//! right?
//! ```compile_fail
//! #[frame::pallet]
//! pub mod pallet {
//! # use frame::prelude::*;
//! # #[pallet::config]
//! # pub trait Config: frame_system::Config {}
//! # #[pallet::pallet]
//! # pub struct Pallet<T>(_);
//! #[derive(codec::Encode, codec::Decode, codec::MaxEncodedLen, scale_info::TypeInfo, Default)]
//! #[scale_info(skip_type_params(T))]
//! pub struct NewType<T: Config>(BlockNumberFor<T>);
//!
//! #[pallet::storage]
//! pub type Something<T: Config> = StorageValue<_, NewType<T>, ValueQuery>;
//! }
//! ```
//!
//! Under the hood, the expansion of the `derive(Default)` will suffer from the same restriction as
//! before: it will only work if `T: Default`, and `T` is not `Default`. Note that this is an
//! expected issue: `T` is merely a wrapper of many other types, such as `BlockNumberFor<T>`.
//! `BlockNumberFor<T>` should indeed implement `Default`, but `T` implementing `Default` is rather
//! meaningless.
//!
//! To fix this, frame provides a set of macros that are analogous to normal rust derive macros, but
//! work nicely on top of structs that are generic over `T: Config`. These macros are:
//!
//! - [`frame::prelude::DefaultNoBound`]
//! - [`frame::prelude::DebugNoBound`]
//! - [`frame::prelude::PartialEqNoBound`]
//! - [`frame::prelude::EqNoBound`]
//! - [`frame::prelude::CloneNoBound`]
//! - [`frame::prelude::PartialOrdNoBound`]
//! - [`frame::prelude::OrdNoBound`]
//!
//! The above traits are almost certainly needed for your tests - to print your type, assert equality
//! or clone it.
//!
//! We can fix the following example by using [`frame::prelude::DefaultNoBound`].
//! ```rust
//! #[frame::pallet]
//! pub mod pallet {
//! # use frame::prelude::*;
//! # #[pallet::config]
//! # pub trait Config: frame_system::Config {}
//! # #[pallet::pallet]
//! # pub struct Pallet<T>(_);
//! #[derive(
//! codec::Encode,
//! codec::Decode,
//! codec::MaxEncodedLen,
//! scale_info::TypeInfo,
//! DefaultNoBound
//! )]
//! #[scale_info(skip_type_params(T))]
//! pub struct NewType<T:Config>(BlockNumberFor<T>);
//!
//! #[pallet::storage]
//! pub type Something<T: Config> = StorageValue<_, NewType<T>, ValueQuery>;
//! }
//! ```
//!
//! Finally, if a custom type that is provided through `Config` is to be stored in the storage, it
//! is subject to the same trait requirements. The following does not work:
//! ```compile_fail
//! #[frame::pallet]
//! pub mod pallet {
//! use frame::prelude::*;
//! #[pallet::config]
//! pub trait Config: frame_system::Config {
//! type CustomType;
//! }
//! #[pallet::pallet]
//! pub struct Pallet<T>(_);
//! #[pallet::storage]
//! pub type Something<T: Config> = StorageValue<_, T::CustomType>;
//! }
//! ```
//!
//! But adding the right trait bounds will fix it.
//! ```rust
//! #[frame::pallet]
//! pub mod pallet {
//! use frame::prelude::*;
//! #[pallet::config]
//! pub trait Config: frame_system::Config {
//! type CustomType: codec::FullCodec
//! + codec::MaxEncodedLen
//! + scale_info::TypeInfo
//! + Debug
//! + Default;
//! }
//! #[pallet::pallet]
//! pub struct Pallet<T>(_);
//! #[pallet::storage]
//! pub type Something<T: Config> = StorageValue<_, T::CustomType>;
//! }
//! ```
@@ -0,0 +1,10 @@
//! # FRAME Accounts
//!
//! 🚧 Work In Progress 🚧
//!
//! How `frame_system` handles accountIds. Nonce. Consumers and Providers, reference counting.
// - poorly understood topics, needs one great article to rul them all.
// - https://github.com/paritytech/substrate/issues/14425
// - https://github.com/paritytech/substrate/pull/12951
// - https://exchange.pezkuwichain.app/questions/263/what-is-the-meaning-of-the-account-provider-sufficients-and-consumer
@@ -0,0 +1,129 @@
// This file is part of pezkuwi-sdk.
//
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # FRAME Tokens
//!
//! This reference doc serves as a high-level overview of the token-related logic in FRAME, and
//! how to properly apply it to your use case.
//!
//! On completion of reading this doc, you should have a good understanding of:
//! - The distinction between token traits and trait implementations in FRAME, and why this
//! distinction is helpful.
//! - Token-related traits available in FRAME.
//! - Token-related trait implementations in FRAME.
//! - How to choose the right trait or trait implementation for your use case.
//! - Where to go next.
//!
//! ## Getting Started
//!
//! The most ubiquitous way to add a token to a FRAME runtime is [`pallet_balances`]. Read
//! more about pallets [here](crate::pezkuwi_sdk::frame_runtime#pallets).
//!
//! You may then write custom pallets that interact with [`pallet_balances`]. The fastest way to
//! get started with that is by
//! [tightly coupling](crate::reference_docs::frame_pallet_coupling#tight-coupling-pallets) your
//! custom pallet to [`pallet_balances`].
//!
//! However, to keep pallets flexible and modular, it is often preferred to
//! [loosely couple](crate::reference_docs::frame_pallet_coupling#loosely--coupling-pallets).
//!
//! To achieve loose coupling,
//! we separate token logic into traits and trait implementations.
//!
//! ## Traits and Trait Implementations
//!
//! Broadly speaking, token logic in FRAME can be divided into two categories: traits and
//! trait implementations.
//!
//! **Traits** define common interfaces that types of tokens should implement. For example, the
//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`) trait specifies an interface
//! for *inspecting* token state such as the total issuance of the token, the balance of individual
//! accounts, etc.
//!
//! **Trait implementations** are concrete implementations of these traits. For example, one of the
//! many traits [`pallet_balances`] implements is
//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`)[^1]. It provides the concrete
//! way of inspecting the total issuance, balance of accounts, etc. There can be many
//! implementations of the same traits.
//!
//! [^1]: Rust Advanced Tip: The knowledge that [`pallet_balances`] implements
//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`) is not some arcane knowledge
//! that you have to know by heart or memorize. One can simply look at the list of the implementors
//! of any trait in the Rust Doc to find all implementors (e.g.
//! [Mutate trait implementors](https://docs.pezkuwichain.io/sdk/master/frame_support/traits/tokens/fungible/trait.Mutate.html#implementors)),
//! or use the `rust-analyzer`'s `Implementations` action.
//!
//! The distinction between traits and trait implementations is helpful because it allows pallets
//! and other logic to be generic over their dependencies, avoiding tight coupling.
//!
//! To illustrate this with an example let's consider [`pallet_preimage`]. This pallet takes a
//! deposit in exchange for storing a preimage for later use. A naive implementation of the
//! pallet may use [`pallet_balances`] in a tightly coupled manner, directly calling methods
//! on the pallet to reserve and unreserve deposits. This approach works well,
//! until someone has a use case requiring that an asset from a different pallet such as
//! [`pallet_assets`] is used for the deposit. Rather than tightly coupling [`pallet_preimage`] to
//! [`pallet_balances`], [`pallet_assets`], and every other token-handling pallet, a user
//! could possibly specify that [`pallet_preimage`] does not specify a concrete pallet as a
//! dependency, but instead accepts any dependency which implements the
//! [`currency::ReservableCurrency`](`frame_support::traits::tokens::currency::ReservableCurrency`)
//! trait, namely via its [`Config::Currency`](`pallet_preimage::pallet::Config::Currency`)
//! associated type. This allows [`pallet_preimage`] to support any arbitrary pallet implementing
//! this trait, without needing any knowledge of what those pallets may be or requiring changes to
//! support new pallets which may be written in the future.
//!
//! Read more about coupling, and the benefits of loose coupling
//! [here](crate::reference_docs::frame_pallet_coupling).
//!
//! ## Fungible Token Traits in FRAME
//!
//! The [`fungible`](`frame_support::traits::fungible`) crate contains the latest set of FRAME
//! fungible token traits, and is recommended to use for all new logic requiring a fungible token.
//! See the crate documentation for more info about these fungible traits.
//!
//! [`fungibles`](`frame_support::traits::fungibles`) provides very similar functionality to
//! [`fungible`](`frame_support::traits::fungible`), except it supports managing multiple tokens.
//!
//! You may notice the trait [`Currency`](`frame_support::traits::Currency`) with similar
//! functionality is also used in the codebase, however this trait is deprecated and existing logic
//! is in the process of being migrated to [`fungible`](`frame_support::traits::fungible`) ([tracking issue](https://github.com/pezkuwichain/pezkuwi-sdk/issues/102)).
//!
//! ## Fungible Token Trait Implementations in FRAME
//!
//! [`pallet_balances`] implements [`fungible`](`frame_support::traits::fungible`), and is the most
//! commonly used fungible implementation in FRAME. Most of the time, it's used for managing the
//! native token of the blockchain network it's used in.
//!
//! [`pallet_assets`] implements [`fungibles`](`frame_support::traits::fungibles`), and is another
//! popular fungible token implementation. It supports the creation and management of multiple
//! assets in a single crate, making it a good choice when a network requires more assets in
//! addition to its native token.
//!
//! ## Non-Fungible Tokens in FRAME
//!
//! [`pallet_nfts`] is recommended to use for all NFT use cases in FRAME.
//! See the crate documentation for more info about this pallet.
//!
//! [`pallet_uniques`] is deprecated and should not be used.
//!
//!
//! # What Next?
//!
//! - If you are interested in implementing a single fungible token, continue reading the
//! [`fungible`](`frame_support::traits::fungible`) and [`pallet_balances`] docs.
//! - If you are interested in implementing a set of fungible tokens, continue reading the
//! [`fungibles`](`frame_support::traits::fungibles`) trait and [`pallet_assets`] docs.
//! - If you are interested in implementing an NFT, continue reading the [`pallet_nfts`] docs.
@@ -0,0 +1,120 @@
//! # Glossary
//!
//! #### State
//!
//! The data around which the blockchain network wishes to come to consensus. Also
//! referred to as "onchain data", "onchain storage" or sometimes just "storage". In UTXO based
//! blockchains, is referred to as "ledger".
//!
//! **Synonyms**: Onchain data, Onchain storage, Storage, Ledger
//!
//! #### State Transition Function
//!
//! The WASM Blob that dictates how the blockchain should transition its state upon encountering new
//! blocks.
//!
//! #### Host
//!
//! The environment that hosts and executes the [state transition function's WASM
//! blob](#state-transition-function).
//!
//! #### Node
//!
//! The full software artifact that contains the [host](#host), but importantly also all the other
//! modules needed to be part of a blockchain network, such as peer-to-peer networking, database and
//! such.
//!
//! **Synonyms**: Client
//!
//! #### Light Node
//!
//! Same as [node](#nodes), but when capable of following the network only through listening to
//! block headers. Usually capable of running in more constrained environments, such as an embedded
//! device, phone, or a web browser.
//!
//! **Synonyms**: Light Client
//!
//! #### Offchain
//!
//! Refers to operations conducted outside the blockchain's consensus mechanism. They are essential
//! for enhancing scalability and efficiency, enabling activities like data fetching and computation
//! without bloating the blockchain state.
//!
//! #### Host Functions:
//!
//! Host functions are the node's API, these are functions provided by the runtime environment (the
//! [host](#host)) to the Wasm runtime. These functions allow the Wasm code to interact with and
//! perform operations on the [node](#node), like accessing the blockchain state.
//!
//! #### Runtime API:
//!
//! This is the API of the runtime, it acts as a communication bridge between the runtime and the
//! node, serving as the exposed interface that facilitates their interactions.
//!
//! #### Dispatchable:
//!
//! Dispatchables are [function objects](https://en.wikipedia.org/wiki/Function_object) that act as
//! the entry points in [FRAME](frame) pallets. They can be called by internal or external entities
//! to interact with the blockchain's state. They are a core aspect of the runtime logic, handling
//! transactions and other state-changing operations.
//!
//! **Synonyms**: Callable
//!
//! #### Extrinsic
//!
//! An extrinsic is a general term for a piece of data that is originated outside of the runtime,
//! included into a block and leads to some action. This includes user-initiated transactions as
//! well as inherents which are placed into the block by the block-builder.
//!
//! #### Pallet
//!
//! Similar to software modules in traditional programming, [FRAME](frame) pallets in Substrate are
//! modular components that encapsulate distinct functionalities or business logic. Just as
//! libraries or modules are used to build and extend the capabilities of a software application,
//! pallets are the foundational building blocks for constructing a blockchain's runtime with frame.
//! They enable the creation of customizable and upgradeable networks, offering a composable
//! framework for a Substrate-based blockchain. Each pallet can be thought of as a plug-and-play
//! module, enhancing the blockchain's functionality in a cohesive and integrated manner.
//!
//! #### Full Node
//!
//! It is a node that prunes historical states, keeping only recent finalized block states to reduce
//! storage needs. Full nodes provide current chain state access and allow direct submission and
//! validation of extrinsics, maintaining network decentralization.
//!
//! #### Archive Node
//!
//! An archive node is a specialized node that maintains a complete history of all block states and
//! transactions. Unlike a full node, it does not prune historical data, ensuring full access to the
//! entire blockchain history. This makes it essential for detailed blockchain analysis and
//! historical queries, but requires significantly more storage capacity.
//!
//! #### Validator
//!
//! A validator is a node that participates in the consensus mechanism of the network.
//! Its role includes block production, transaction validation, network integrity and security
//! maintenance.
//!
//! #### Collator
//!
//! A collator is a node that is responsible for producing candidate blocks for the validators.
//! Collators are similar to validators on any other blockchain but, they do not need to provide
//! security guarantees as the Relay Chain handles this.
//!
//! #### Teyrchain
//!
//! Short for "parallelized chain" a teyrchain is a specialized blockchain that runs in parallel to
//! the Relay Chain (Pezkuwi, Kusama, etc.), benefiting from the shared security and
//! interoperability features of it.
//!
//! **Synonyms**: AppChain
//!
//! #### PVF
//! The Teyrchain Validation Function (PVF) is the current runtime Wasm for a teyrchain that is
//! stored on the Relay chain. It is an essential component in the Pezkuwi ecosystem, encapsulating
//! the validation logic for each teyrchain. The PVF is executed by validators to verify the
//! correctness of teyrchain blocks. This is critical for ensuring that each block follows the logic
//! set by its respective teyrchain, thus maintaining the integrity and security of the entire
//! network.
//!
//! **Synonyms**: Teyrchain Validation Function
@@ -0,0 +1,25 @@
//! # Metadata
//!
//! The existence of metadata in pezkuwi-sdk goes back to the (forkless) upgrade-ability of all
//! Substrate-based blockchains, which is achieved through
//! [`crate::reference_docs::wasm_meta_protocol`]. You can learn more about the details of how to
//! deal with these upgrades in [`crate::reference_docs::frame_runtime_upgrades_and_migrations`].
//!
//! Another consequence of upgrade-ability is that as a UI, wallet, or generally an offchain entity,
//! it is hard to know the types internal to the runtime, specifically in light of the fact that
//! they can change at any point in time.
//!
//! This is why all Substrate-based runtimes must expose a [`sp_api::Metadata`] api, which mandates
//! the runtime to return a description of itself. The return type of this api is `Vec<u8>`, meaning
//! that it is up to the runtime developer to decide on the format of this.
//!
//! All [`crate::pezkuwi_sdk::frame_runtime`] based runtimes expose a specific metadata language,
//! maintained in <https://github.com/paritytech/frame-metadata> which is adopted in the Pezkuwi
//! ecosystem.
//!
//! ## Metadata Explorers:
//!
//! A few noteworthy tools that inspect the (FRAME-based) metadata of a chain:
//!
//! - <https://wiki.network.pezkuwichain.io/docs/metadata>
//! - <https://paritytech.github.io/subxt-explorer/>
@@ -0,0 +1,116 @@
//! # Pezkuwi SDK Reference Docs.
//!
//! This is the entry point for all reference documents that enhance one's learning experience in
//! the Pezkuwi SDK.
//!
//! Note that this module also contains the [glossary](crate::reference_docs::glossary).
//!
//! ## What is a "reference document"?
//!
//! First, see [why we use rust-docs for everything](crate::meta_contributing#why-rust-docs) and our
//! documentation [principles](crate::meta_contributing#principles). We acknowledge that as much of
//! the crucial information should be embedded in the low level rust-docs. Then, high level
//! scenarios should be covered in [`crate::guides`]. Finally, we acknowledge that there is a
//! category of information that is:
//!
//! 1. Crucial to know.
//! 2. Is too high level to be in the rust-doc of any one `type`, `trait` or `fn`.
//! 3. Is too low level to be encompassed in a [`crate::guides`].
//!
//! We call this class of documents "reference documents". Our goal should be to minimize the number
//! of "reference" docs, as they incur maintenance burden.
/// Learn how Substrate and FRAME use traits and associated types to make modules generic in a
/// type-safe manner.
pub mod trait_based_programming;
/// Learn about the way Substrate and FRAME view their blockchains as state machines.
pub mod blockchain_state_machines;
/// The glossary.
pub mod glossary;
/// Learn about the WASM meta-protocol of all Substrate-based chains.
pub mod wasm_meta_protocol;
/// Learn about the differences between smart contracts and a FRAME-based runtime. They are both
/// "code stored onchain", but how do they differ?
pub mod runtime_vs_smart_contract;
/// Learn about how extrinsics are encoded to be transmitted to a node and stored in blocks.
pub mod extrinsic_encoding;
/// Deprecated in favor of transaction extensions.
pub mod signed_extensions;
/// Learn about the transaction extensions that form a part of extrinsics.
pub mod transaction_extensions;
/// Learn about *Origins*, a topic in FRAME that enables complex account abstractions to be built.
pub mod frame_origin;
/// Learn about the details of what derives are needed for a type to be store-able in `frame`
/// storage.
pub mod frame_storage_derives;
/// Learn about how to write safe and defensive code in your FRAME runtime.
pub mod defensive_programming;
/// Learn about composite enums and other runtime level types, such as `RuntimeEvent` and
/// `RuntimeCall`.
pub mod frame_runtime_types;
/// Learn about how to make a pallet/runtime that is fee-less and instead uses another mechanism to
/// control usage and sybil attacks.
pub mod fee_less_runtime;
/// Learn about metadata, the main means through which an upgradeable runtime communicates its
/// properties to the outside world.
pub mod metadata;
/// Learn about how to add custom host functions to the node.
pub mod custom_host_functions;
/// Learn about how frame-system handles `account-ids`, nonces, consumers and providers.
pub mod frame_system_accounts;
/// Advice for configuring your development environment for Substrate development.
pub mod development_environment_advice;
/// Learn about benchmarking and weight.
pub mod frame_benchmarking_weight;
/// Learn about the token-related logic in FRAME and how to apply it to your use case.
pub mod frame_tokens;
/// Learn about chain specification file and the genesis state of the blockchain.
pub mod chain_spec_genesis;
/// Learn about Substrate's CLI, and how it can be extended.
pub mod cli;
/// Learn about Runtime Upgrades and best practices for writing Migrations.
pub mod frame_runtime_upgrades_and_migrations;
/// Learn about the offchain workers, how they function, and how to use them, as provided by the
/// [`frame`] APIs.
pub mod frame_offchain_workers;
/// Learn about the different ways through which multiple [`frame`] pallets can be combined to work
/// together.
pub mod frame_pallet_coupling;
/// Learn about how to do logging in FRAME-based runtimes.
pub mod frame_logging;
/// Learn about the Pezkuwi Umbrella crate that re-exports all other crates.
pub mod umbrella_crate;
/// Learn about how to create custom RPC endpoints and runtime APIs.
pub mod custom_runtime_api_rpc;
/// The [`pezkuwi-omni-node`](https://crates.io/crates/polkadot-omni-node) and its related binaries.
pub mod omni_node;
/// Learn about the state in Substrate.
pub mod state;
@@ -0,0 +1,201 @@
//! # (Omni) Node
//!
//! This reference doc elaborates on what a Pezkuwi-SDK/Substrate node software is, and what
//! various ways exist to run one.
//!
//! The node software, as denoted in [`crate::reference_docs::wasm_meta_protocol`], is everything in
//! a blockchain other than the WASM runtime. It contains common components such as the database,
//! networking, RPC server and consensus. Substrate-based nodes are native binaries that are
//! compiled down from the Rust source code. The `node` folder in any of the [`templates`] are
//! examples of this source.
//!
//! > Note: A typical node also contains a lot of other tools (exposed as subcommands) that are
//! > useful for operating a blockchain, such as the ones noted in
//! > [`pezkuwi_omni_node_lib::cli::Cli::subcommand`].
//!
//! ## Node <> Runtime Interdependence
//!
//! While in principle the node can be mostly independent of the runtime, for various reasons, such
//! as the [native runtime](crate::reference_docs::wasm_meta_protocol#native-runtime), the node and
//! runtime were historically tightly linked together. Another reason is that the node and the
//! runtime need to be in agreement about which consensus algorithm they use, as described
//! [below](#consensus-engine).
//!
//! Specifically, the node relied on the existence of a linked runtime, and *could only reliably run
//! that runtime*. This is why if you look at any of the [`templates`], they are all composed of a
//! node, and a runtime.
//!
//! Moreover, the code and API of each of these nodes was historically very advanced, and tailored
//! towards those who wish to customize many of the node components at depth.
//!
//! > The notorious `service.rs` in any node template is a good example of this.
//!
//! A [trend](https://github.com/pezkuwichain/pezkuwi-sdk/issues/97) has already been undergoing in
//! order to de-couple the node and the runtime for a long time. The north star of this effort is
//! twofold :
//!
//! 1. develop what can be described as an "omni-node": A node that can run most runtimes.
//! 2. provide a cleaner abstraction for creating a custom node.
//!
//! While a single omni-node running *all possible runtimes* is not feasible, the
//! [`pezkuwi-omni-node`] is an attempt at creating the former, and the [`pezkuwi_omni_node_lib`]
//! is the latter.
//!
//! > Note: The OmniNodes are mainly focused on the development needs of **Pezkuwi
//! > teyrchains ONLY**, not (Substrate) solo-chains. For the time being, solo-chains are not
//! > supported by the OmniNodes. This might change in the future.
//!
//! ## Types of Nodes
//!
//! With the emergence of the OmniNodes, let's look at the various Node options available to a
//! builder.
//!
//! ### [`pezkuwi-omni-node`]
//!
//! [`pezkuwi-omni-node`] is a white-labeled binary, released as a part of Pezkuwi SDK that is
//! capable of meeting the needs of most Pezkuwi teyrchains.
//!
//! It can act as the collator of a teyrchain in production, with all the related auxillary
//! functionalities that a normal collator node has: RPC server, archiving state, etc. Moreover, it
//! can also run the wasm blob of the teyrchain locally for testing and development.
//!
//! ### [`pezkuwi_omni_node_lib`]
//!
//! [`pezkuwi_omni_node_lib`] is the library version of the above, which can be used to create a
//! fresh teyrchain node, with a some limited configuration options using a lean API.
//!
//! ### Old School Nodes
//!
//! The existing node architecture, as seen in the [`templates`], is still available for those who
//! want to have full control over the node software.
//!
//! ### Summary
//!
//! We can summarize the choices for the node software of any given user of Pezkuwi-SDK, wishing to
//! deploy a teyrchain into 3 categories:
//!
//! 1. **Use the [`pezkuwi-omni-node`]**: This is the easiest way to get started, and is the most
//! likely to be the best choice for most users.
//! * can run almost any runtime with [`--dev-block-time`]
//! 2. **Use the [`pezkuwi_omni_node_lib`]**: This is the best choice for those who want to have
//! slightly more control over the node software, such as embedding a custom chain-spec.
//! 3. **Use the old school nodes**: This is the best choice for those who want to have full control
//! over the node software, such as changing the consensus engine, altering the transaction pool,
//! and so on.
//!
//! ## _OmniTools_: User Journey
//!
//! All in all, the user journey of a team/builder, in the OmniNode world is as follows:
//!
//! * The [`templates`], most notably the [`teyrchain-template`] is the canonical starting point.
//! That being said, the node code of the templates (which may be eventually
//! removed/feature-gated) is no longer of relevance. The only focus is in the runtime, and
//! obtaining a `.wasm` file. References:
//! * [`crate::guides::your_first_pallet`]
//! * [`crate::guides::your_first_runtime`]
//! * If need be, the weights of the runtime need to be updated using `frame-omni-bencher`.
//! References:
//! * [`crate::reference_docs::frame_benchmarking_weight`]
//! * Next, [`chain-spec-builder`] is used to generate a `chain_spec.json`, either for development,
//! or for production. References:
//! * [`crate::reference_docs::chain_spec_genesis`]
//! * For local development, the following options are available:
//! * `pezkuwi-omni-node` (notably, with [`--dev-block-time`]). References:
//! * [`crate::guides::your_first_node`]
//! * External tools such as `chopsticks`, `zombienet`.
//! * See the `README.md` file of the `pezkuwi-sdk-teyrchain-template`.
//! * For production `pezkuwi-omni-node` can be used out of the box.
//! * For further customization [`pezkuwi_omni_node_lib`] can be used.
//!
//! ## Appendix
//!
//! This section describes how the interdependence between the node and the runtime is related to
//! the consensus engine. This information is useful for those who want to understand the
//! historical context of the node and the runtime.
//!
//! ### Consensus Engine
//!
//! In any given substrate-based chain, both the node and the runtime will have their own
//! opinion/information about what consensus engine is going to be used.
//!
//! In practice, the majority of the implementation of any consensus engine is in the node side, but
//! the runtime also typically needs to expose a custom runtime-api to enable the particular
//! consensus engine to work, and that particular runtime-api is implemented by a pallet
//! corresponding to that consensus engine.
//!
//! For example, taking a snippet from [`solochain_template_runtime`], the runtime has to provide
//! this additional runtime-api (compared to [`minimal_template_runtime`]), if the node software is
//! configured to use the Aura consensus engine:
//!
//! ```text
//! impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
//! fn slot_duration() -> sp_consensus_aura::SlotDuration {
//! ...
//! }
//! fn authorities() -> Vec<AuraId> {
//! ...
//! }
//! }
//! ```
//!
//! For simplicity, we can break down "consensus" into two main parts:
//!
//! * Block Authoring: Deciding who gets to produce the next block.
//! * Finality: Deciding when a block is considered final.
//!
//! For block authoring, there are a number of options:
//!
//! * [`sc_consensus_manual_seal`]: Useful for testing, where any node can produce a block at any
//! time. This is often combined with a fixed interval at which a block is produced.
//! * [`sc_consensus_aura`]/[`pallet_aura`]: A simple round-robin block authoring mechanism.
//! * [`sc_consensus_babe`]/[`pallet_babe`]: A more advanced block authoring mechanism, capable of
//! anonymizing the next block author.
//! * [`sc_consensus_pow`]: Proof of Work block authoring.
//!
//! For finality, there is one main option shipped with pezkuwi-sdk:
//!
//! * [`sc_consensus_grandpa`]/[`pallet_grandpa`]: A finality gadget that uses a voting mechanism to
//! decide when a block
//!
//! **The most important lesson here is that the node and the runtime must have matching consensus
//! components.**
//!
//! ### Consequences for OmniNode
//!
//!
//! The consequence of the above is that anyone using the OmniNode must also be aware of the
//! consensus system used in the runtime, and be aware if it is matching that of the OmniNode or
//! not. For the time being, [`pezkuwi-omni-node`] only supports:
//!
//! * Teyrchain-based Aura consensus, with 6s async-backing block-time, and before full elastic
//! scaling). [`pezkuwi_omni_node_lib::cli::Cli::experimental_use_slot_based`] for fixed factor
//! scaling (a step
//! * Ability to run any runtime with [`--dev-block-time`] flag. This uses
//! [`sc_consensus_manual_seal`] under the hood, and has no restrictions on the runtime's
//! consensus.
//!
//! [This](https://github.com/pezkuwichain/pezkuwi-sdk/issues/143) future improvement to OmniNode
//! aims to make such checks automatic.
//!
//! ### Runtime conventions
//!
//! The Omni Node needs to make some assumptions about the runtime. During startup, the node fetches
//! the runtime metadata and asserts that the runtime represents a compatible teyrchain.
//! The checks are best effort and will generate warning level logs in the Omni Node log file on
//! failure.
//!
//! The list of checks may evolve in the future and for now only few rules are implemented:
//! * runtimes must define a type for [`cumulus-pallet-teyrchain-system`], which is recommended to
//! be named as `TeyrchainSystem`.
//! * runtimes must define a type for [`frame-system`] pallet, which is recommended to be named as
//! `System`. The configured [`block number`] here will be used by Omni Node to configure AURA
//! accordingly.
//!
//! [`templates`]: crate::pezkuwi_sdk::templates
//! [`teyrchain-template`]: https://github.com/pezkuwichain/pezkuwi-sdk-teyrchain-template
//! [`--dev-block-time`]: pezkuwi_omni_node_lib::cli::Cli::dev_block_time
//! [`pezkuwi-omni-node`]: https://crates.io/crates/polkadot-omni-node
//! [`chain-spec-builder`]: https://crates.io/crates/staging-chain-spec-builder
//! [`cumulus-pallet-teyrchain-system`]: https://docs.rs/cumulus-pallet-parachain-system/latest/cumulus_pallet_parachain_system/
//! [`frame-system`]: https://docs.rs/frame-system/latest/frame_system/
//! [`block number`]: https://docs.rs/frame-system/latest/frame_system/pallet/storage_types/struct.Number.html
@@ -0,0 +1,209 @@
//! # Runtime vs. Smart Contracts
//!
//! *TL;DR*: If you need to create a *Blockchain*, then write a runtime. If you need to create a
//! *DApp*, then write a Smart Contract.
//!
//! This is a comparative analysis of Substrate-based Runtimes and Smart Contracts, highlighting
//! their main differences. Our aim is to equip you with a clear understanding of how these two
//! methods of deploying on-chain logic diverge in their design, usage, and implications.
//!
//! Both Runtimes and Smart Contracts serve distinct purposes. Runtimes offer deep customization for
//! blockchain development, while Smart Contracts provide a more accessible approach for
//! decentralized applications. Understanding their differences is crucial in choosing the right
//! approach for a specific solution.
//!
//! ## Substrate
//! Substrate is a modular framework that enables the creation of purpose-specific blockchains. In
//! the Pezkuwi ecosystem you can find two distinct approaches for on-chain code execution:
//! [Runtime Development](#runtime-in-substrate) and [Smart Contracts](#smart-contracts).
//!
//! #### Smart Contracts in Substrate
//! Smart Contracts are autonomous, programmable constructs deployed on the blockchain.
//! In [FRAME](frame), Smart Contracts infrastructure is implemented by the
//! [`pallet_contracts`] for WASM-based contracts or the
//! [`pallet_evm`](https://github.com/polkadot-evm/frontier/tree/master/frame/evm) for EVM-compatible contracts. These pallets
//! enable Smart Contract developers to build applications and systems on top of a Substrate-based
//! blockchain.
//!
//! #### Runtime in Substrate
//! The Runtime is the state transition function of a Substrate-based blockchain. It defines the
//! rules for processing transactions and blocks, essentially governing the behavior and
//! capabilities of a blockchain.
//!
//! ## Comparative Table
//!
//! | Aspect | Runtime | Smart Contracts |
//! |-----------------------|-------------------------------------------------------------------------|----------------------------------------------------------------------|
//! | **Design Philosophy** | Core logic of a blockchain, allowing broad and deep customization. | Designed for DApps deployed on the blockchain runtime. |
//! | **Development Complexity** | Requires in-depth knowledge of Rust and Substrate. Suitable for complex blockchain architectures. | Easier to develop with knowledge of Smart Contract languages like Solidity or [ink!](https://use.ink/). |
//! | **Upgradeability and Flexibility** | Offers comprehensive upgradeability with migration logic and on-chain governance, allowing modifications to the entire blockchain logic without hard forks. | Less flexible in upgrade migrations but offers more straightforward deployment and iteration. |
//! | **Performance and Efficiency** | More efficient, optimized for specific needs of the blockchain. | Can be less efficient due to its generic nature (e.g. the overhead of a virtual machine). |
//! | **Security Considerations** | Security flaws can affect the entire blockchain. | Security risks usually localized to the individual contract. |
//! | **Weighing and Metering** | Operations can be weighed, allowing for precise benchmarking. | Execution is metered, allowing for measurement of resource consumption. |
//!
//! We will now explore these differences in more detail.
//!
//! ## Design Philosophy
//! Runtimes and Smart Contracts are designed for different purposes. Runtimes are the core logic
//! of a blockchain, while Smart Contracts are designed for DApps on top of the blockchain.
//! Runtimes can be more complex, but also more flexible and efficient, while Smart Contracts are
//! easier to develop and deploy.
//!
//! #### Runtime Design Philosophy
//! - **Core Blockchain Logic**: Runtimes are essentially the backbone of a blockchain. They define
//! the fundamental rules, operations, and state transitions of the blockchain network.
//! - **Broad and Deep Customization**: Runtimes allow for extensive customization and flexibility.
//! Developers can tailor the most fundamental aspects of the blockchain, like introducing an
//! efficient transaction fee model to eliminating transaction fees completely. This level of
//! control is essential for creating specialized or application-specific blockchains.
//!
//! #### Smart Contract Design Philosophy
//! - **DApps Development**: Smart contracts are designed primarily for developing DApps. They
//! operate on top of the blockchain's infrastructure.
//! - **Modularity and Isolation**: Smart contracts offer a more modular approach. Each contract is
//! an isolated piece of code, executing predefined operations when triggered. This isolation
//! simplifies development and enhances security, as flaws in one contract do not directly
//! compromise the entire network.
//!
//! ## Development Complexity
//! Runtimes and Smart Contracts differ in their development complexity, largely due to their
//! differing purposes and technical requirements.
//!
//! #### Runtime Development Complexity
//! - **In-depth Knowledge Requirements**: Developing a Runtime in Substrate requires a
//! comprehensive understanding of Rust, Substrate's framework, and blockchain principles.
//! - **Complex Blockchain Architectures**: Runtime development is suitable for creating complex
//! blockchain architectures. Developers must consider aspects like security, scalability, and
//! network efficiency.
//!
//! #### Smart Contract Development Complexity
//! - **Accessibility**: Smart Contract development is generally more accessible, especially for
//! those already familiar with programming concepts. Knowledge of smart contract-specific
//! languages like Solidity or ink! is required.
//! - **Focused on Application Logic**: The development here is focused on the application logic
//! only. This includes writing functions that execute when certain conditions are met, managing
//! state within the contract, and ensuring security against common Smart Contract
//! vulnerabilities.
//!
//! ## Upgradeability and Flexibility
//! Runtimes and Smart Contracts differ significantly in how they handle upgrades and flexibility,
//! each with its own advantages and constraints. Runtimes are more flexible, allowing for writing
//! migration logic for upgrades, while Smart Contracts are less flexible but offer easier
//! deployment and iteration.
//!
//! #### Runtime Upgradeability and Flexibility
//! - **Migration Logic**: One of the key strengths of runtime development is the ability to define
//! migration logic. This allows developers to implement changes in the state or structure of the
//! blockchain during an upgrade. Such migrations can adapt the existing state to fit new
//! requirements or features seamlessly.
//! - **On-Chain Governance**: Upgrades in a Runtime environment are typically governed on-chain,
//! involving validators or a governance mechanism. This allows for a democratic and transparent
//! process for making substantial changes to the blockchain.
//! - **Broad Impact of Changes**: Changes made in Runtime affect the entire blockchain. This gives
//! developers the power to introduce significant improvements or changes but also necessitates a
//! high level of responsibility and scrutiny, we will talk further about it in the [Security
//! Considerations](#security-considerations) section.
//!
//! #### Smart Contract Upgradeability and Flexibility
//! - **Deployment and Iteration**: Smart Contracts, by nature, are designed for more
//! straightforward deployment and iteration. Developers can quickly deploy contracts.
//! - **Contract Code Updates**: Once deployed, although typically immutable, Smart Contracts can be
//! upgraded, but lack of migration logic. The [`pallet_contracts`]
//! allows for contracts to be upgraded by exposing the `set_code` dispatchable. More details on this
//! can be found in [Ink! documentation on upgradeable contracts](https://use.ink/basics/upgradeable-contracts).
//! - **Isolated Impact**: Upgrades or changes to a smart contract generally impact only that
//! contract and its users, unlike Runtime upgrades that have a network-wide effect.
//! - **Simplicity and Rapid Development**: The development cycle for Smart Contracts is usually
//! faster and less complex than Runtime development, allowing for rapid prototyping and
//! deployment.
//!
//! ## Performance and Efficiency
//! Runtimes and Smart Contracts have distinct characteristics in terms of performance and
//! efficiency due to their inherent design and operational contexts. Runtimes are more efficient
//! and optimized for specific needs, while Smart Contracts are more generic and less efficient.
//!
//! #### Runtime Performance and Efficiency
//! - **Optimized for Specific Needs**: Runtime modules in Substrate are tailored to meet the
//! specific needs of the blockchain. They are integrated directly into the blockchain's core,
//! allowing them to operate with high efficiency and minimal overhead.
//! - **Direct Access to Blockchain State**: Runtime has direct access to the blockchain's state.
//! This direct access enables more efficient data processing and transaction handling, as there
//! is no additional layer between the runtime logic and the blockchain's core.
//! - **Resource Management**: Resource management is integral to runtime development to ensure that
//! the blockchain operates smoothly and efficiently.
//!
//! #### Smart Contract Performance and Efficiency
//! - **Generic Nature and Overhead**: Smart Contracts, particularly those running in virtual
//! machine environments, can be less efficient due to the generic nature of their execution
//! environment. The overhead of the virtual machine can lead to increased computational and
//! resource costs.
//! - **Isolation and Security Constraints**: Smart Contracts operate in an isolated environment to
//! ensure security and prevent unwanted interactions with the blockchain's state. This isolation,
//! while crucial for security, can introduce additional computational overhead.
//! - **Gas Mechanism and Metering**: The gas mechanism in Smart Contracts, used for metering
//! computational resources, ensures that contracts don't consume excessive resources. However,
//! this metering itself requires computational power, adding to the overall cost of contract
//! execution.
//!
//! ## Security Considerations
//! These two methodologies, while serving different purposes, come with their own unique security
//! considerations.
//!
//! #### Runtime Security Aspects
//! Runtimes, being at the core of blockchain functionality, have profound implications for the
//! security of the entire network:
//!
//! - **Broad Impact**: Security flaws in the runtime can compromise the entire blockchain,
//! affecting all network participants.
//! - **Governance and Upgradeability**: Runtime upgrades, while powerful, need rigorous governance
//! and testing to ensure security. Improperly executed upgrades can introduce vulnerabilities or
//! disrupt network operations.
//! - **Complexity and Expertise**: Developing and maintaining runtime requires a higher level of
//! expertise in blockchain architecture and security, as mistakes can be far-reaching.
//!
//! #### Smart Contract Security Aspects
//! Smart contracts, while more isolated, bring their own set of security challenges:
//!
//! - **Isolated Impact**: Security issues in a smart contract typically affect the contract itself
//! and its users, rather than the whole network.
//! - **Contract-specific Risks**: Common issues like reentrancy
//! attacks, improper handling of external calls, and gas limit vulnerabilities are specific to
//! smart contract development.
//! - **Permissionless Deployment**: Since anyone can deploy a smart contract,
//! the ecosystem is more open to potentially malicious or vulnerable code.
//!
//! ## Weighing and Metering
//! Weighing and metering are mechanisms designed to limit the resources used by external actors.
//! However, there are fundamental differences in how these resources are handled in FRAME-based
//! Runtimes and how they are handled in Smart Contracts, while Runtime operations are weighed,
//! Smart Contract executions must be metered.
//!
//! #### Weighing
//! In FRAME-based Runtimes, operations are *weighed*. This means that each operation in the Runtime
//! has a fixed upper cost, known in advance, determined through
//! [benchmarking](crate::reference_docs::frame_benchmarking_weight). Weighing is practical here
//! because:
//!
//! - *Predictability*: Runtime operations are part of the blockchain's core logic, which is static
//! until an upgrade occurs. This predictability allows for precise
//! [benchmarking](crate::reference_docs::frame_benchmarking_weight).
//! - *Prevention of Abuse*: By having a fixed upper cost that corresponds to the worst-case
//! complexity scenario of its execution (and a mechanism to refund unused weight), it becomes
//! infeasible for an attacker to create transactions that could unpredictably consume excessive
//! resources.
//!
//! #### Metering
//! For Smart Contracts resource consumption is metered. This is essential due to:
//!
//! - **Untrusted Nature**: Unlike Runtime operations, Smart Contracts can be deployed by any user,
//! and their behavior isnt known in advance. Metering dynamically measures resource consumption
//! as the contract executes.
//! - **Safety Against Infinite Loops**: Metering protects the blockchain from poorly designed
//! contracts that might run into infinite loops, consuming an indefinite amount of resources.
//!
//! #### Implications for Developers and Users
//! - **For Runtime Developers**: Understanding the cost of each operation is essential. Misjudging
//! the weight of operations can lead to network congestion or vulnerability exploitation.
//! - **For Smart Contract Developers**: Being mindful of the gas cost associated with contract
//! execution is crucial. Efficiently written contracts save costs and are less likely to hit gas
//! limits, ensuring smoother execution on the blockchain.
@@ -0,0 +1,2 @@
//! `SignedExtension`s are deprecated in favor of
//! [`TransactionExtension`s](crate::reference_docs::transaction_extensions).
@@ -0,0 +1,12 @@
//! # State
//!
//! The state is abstracted as a key-value like database. Every item that
//! needs to be persisted by the [State Transition
//! Function](crate::reference_docs::blockchain_state_machines) is written to the state.
//!
//! ## Special keys
//!
//! The key-value pairs in the state are represented as byte sequences. The node
//! doesn't know how to interpret most the key-value pairs. However, there exist some
//! special keys and its values that are known to the node, the so-called
//! [`well-known-keys`](sp_storage::well_known_keys).
@@ -0,0 +1,229 @@
//! # Trait-based Programming
//!
//! This document walks you over a peculiar way of using Rust's `trait` items. This pattern is
//! abundantly used within [`frame`] and is therefore paramount important for a smooth transition
//! into it.
//!
//! The rest of this document assumes familiarity with the
//! [Rust book's Advanced Traits](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html)
//! section.
//! Moreover, we use the [`frame::traits::Get`].
//!
//! First, imagine we are writing a FRAME pallet. We represent this pallet with a `struct Pallet`,
//! and this pallet wants to implement the functionalities of that pallet, for example a simple
//! `transfer` function. For the sake of education, we are interested in having a `MinTransfer`
//! amount, expressed as a [`frame::traits::Get`], which will dictate what is the minimum amount
//! that can be transferred.
//!
//! We can foremost write this as simple as the following snippet:
#![doc = docify::embed!("./src/reference_docs/trait_based_programming.rs", basic)]
//!
//!
//! In this example, we use arbitrary choices for `AccountId`, `Balance` and the `MinTransfer` type.
//! This works great for **one team's purposes** but we have to remember that Substrate and FRAME
//! are written as generic frameworks, intended to be highly configurable.
//!
//! In a broad sense, there are two avenues in exposing configurability:
//!
//! 1. For *values* that need to be generic, for example `MinTransfer`, we attach them to the
//! `Pallet` struct as fields:
//!
//! ```
//! struct Pallet {
//! min_transfer: u128,
//! }
//! ```
//!
//! 2. For *types* that need to be generic, we would have to use generic or associated types, such
//! as:
//!
//! ```
//! struct Pallet<AccountId> {
//! min_transfer: u128,
//! _marker: std::marker::PhantomData<AccountId>,
//! }
//! ```
//!
//! Substrate and FRAME, for various reasons (performance, correctness, type safety) has opted to
//! use *types* to declare both *values* and *types* as generic. This is the essence of why the
//! `Get` trait exists.
//!
//! This would bring us to the second iteration of the pallet, which would look like:
#![doc = docify::embed!("./src/reference_docs/trait_based_programming.rs", generic)]
//!
//! In this example, we managed to make all 3 of our types generic. Taking the example of the
//! `AccountId`, one should read the above as following:
//!
//! > The `Pallet` does not know what type `AccountId` concretely is, but it knows that it is
//! > something that adheres to being `From<[u8; 32]>`.
//!
//! This method would work, but it suffers from two downsides:
//!
//! 1. It is verbose, each `impl` block would have to reiterate all of the trait bounds.
//! 2. It cannot easily share/inherit generic types. Imagine multiple pallets wanting to be generic
//! over a single `AccountId`. There is no easy way to express that in this model.
//!
//! Finally, this brings us to using traits and associated types on traits to express the above.
//! Trait associated types have the benefit of:
//!
//! 1. Being less verbose, as in effect they can *group multiple `type`s together*.
//! 2. Can inherit from one another by declaring
//! [supertraits](https://doc.rust-lang.org/rust-by-example/trait/supertraits.html).
//!
//! > Interestingly, one downside of associated types is that declaring defaults on them is not
//! > stable yet. In the meantime, we have built our own custom mechanics around declaring defaults
//! for associated types, see [`pallet_default_config_example`].
//!
//! The last iteration of our code would look like this:
#![doc = docify::embed!("./src/reference_docs/trait_based_programming.rs", trait_based)]
//!
//! Notice how instead of having multiple generics, everything is generic over a single `<T:
//! Config>`, and all types are fetched through `T`, for example `T::AccountId`, `T::MinTransfer`.
//!
//! Finally, imagine all pallets wanting to be generic over `AccountId`. This can be achieved by
//! having individual `trait Configs` declare a shared `trait SystemConfig` as their
//! [supertrait](https://doc.rust-lang.org/rust-by-example/trait/supertraits.html).
#![doc = docify::embed!("./src/reference_docs/trait_based_programming.rs", with_system)]
//! In FRAME, this shared supertrait is [`frame::prelude::frame_system`].
//!
//! Notice how this made no difference in the syntax of the rest of the code. `T::AccountId` is
//! still a valid type, since `T` implements `Config` and `Config` implies `SystemConfig`, which
//! has a `type AccountId`.
//!
//! Note, in some instances one would need to use what is known as the fully-qualified-syntax to
//! access a type to help the Rust compiler disambiguate.
#![doc = docify::embed!("./src/reference_docs/trait_based_programming.rs", fully_qualified)]
//!
//! This syntax can sometimes become more complicated when you are dealing with nested traits.
//! Consider the following example, in which we fetch the `type Balance` from another trait
//! `CurrencyTrait`.
#![doc = docify::embed!("./src/reference_docs/trait_based_programming.rs", fully_qualified_complicated)]
//!
//! Notice the final `type BalanceOf` and how it is defined. Using such aliases to shorten the
//! length of fully qualified syntax is a common pattern in FRAME.
//!
//! The above example is almost identical to the well-known (and somewhat notorious) `type
//! BalanceOf` that is often used in the context of [`frame::traits::fungible`].
#![doc = docify::embed!("../../substrate/frame/fast-unstake/src/types.rs", BalanceOf)]
//!
//! ## Additional Resources
//!
//! - <https://github.com/paritytech/substrate/issues/13836>
//! - [Substrate Seminar - Traits and Generic Types](https://www.youtube.com/watch?v=6cp10jVWNl4)
//! - <https://exchange.pezkuwichain.app/questions/2228/type-casting-to-trait-t-as-config>
#![allow(unused)]
use frame::traits::Get;
#[docify::export]
mod basic {
struct Pallet;
type AccountId = frame::deps::sp_runtime::AccountId32;
type Balance = u128;
type MinTransfer = frame::traits::ConstU128<10>;
impl Pallet {
fn transfer(_from: AccountId, _to: AccountId, _amount: Balance) {
todo!()
}
}
}
#[docify::export]
mod generic {
use super::*;
struct Pallet<AccountId, Balance, MinTransfer> {
_marker: std::marker::PhantomData<(AccountId, Balance, MinTransfer)>,
}
impl<AccountId, Balance, MinTransfer> Pallet<AccountId, Balance, MinTransfer>
where
Balance: frame::traits::AtLeast32BitUnsigned,
MinTransfer: frame::traits::Get<Balance>,
AccountId: From<[u8; 32]>,
{
fn transfer(_from: AccountId, _to: AccountId, amount: Balance) {
assert!(amount >= MinTransfer::get());
unimplemented!();
}
}
}
#[docify::export]
mod trait_based {
use super::*;
trait Config {
type AccountId: From<[u8; 32]>;
type Balance: frame::traits::AtLeast32BitUnsigned;
type MinTransfer: frame::traits::Get<Self::Balance>;
}
struct Pallet<T: Config>(std::marker::PhantomData<T>);
impl<T: Config> Pallet<T> {
fn transfer(_from: T::AccountId, _to: T::AccountId, amount: T::Balance) {
assert!(amount >= T::MinTransfer::get());
unimplemented!();
}
}
}
#[docify::export]
mod with_system {
use super::*;
pub trait SystemConfig {
type AccountId: From<[u8; 32]>;
}
pub trait Config: SystemConfig {
type Balance: frame::traits::AtLeast32BitUnsigned;
type MinTransfer: frame::traits::Get<Self::Balance>;
}
pub struct Pallet<T: Config>(std::marker::PhantomData<T>);
impl<T: Config> Pallet<T> {
fn transfer(_from: T::AccountId, _to: T::AccountId, amount: T::Balance) {
assert!(amount >= T::MinTransfer::get());
unimplemented!();
}
}
}
#[docify::export]
mod fully_qualified {
use super::with_system::*;
// Example of using fully qualified syntax.
type AccountIdOf<T> = <T as SystemConfig>::AccountId;
}
#[docify::export]
mod fully_qualified_complicated {
use super::with_system::*;
trait CurrencyTrait {
type Balance: frame::traits::AtLeast32BitUnsigned;
fn more_stuff() {}
}
trait Config: SystemConfig {
type Currency: CurrencyTrait;
}
struct Pallet<T: Config>(std::marker::PhantomData<T>);
impl<T: Config> Pallet<T> {
fn transfer(
_from: T::AccountId,
_to: T::AccountId,
_amount: <<T as Config>::Currency as CurrencyTrait>::Balance,
) {
unimplemented!();
}
}
/// A common pattern in FRAME.
type BalanceOf<T> = <<T as Config>::Currency as CurrencyTrait>::Balance;
}
@@ -0,0 +1,105 @@
//! Transaction extensions are, briefly, a means for different chains to extend the "basic"
//! extrinsic format with custom data that can be checked by the runtime.
//!
//! # FRAME provided transaction extensions
//!
//! FRAME by default already provides the following transaction extensions:
//!
//! - [`CheckGenesis`](frame_system::CheckGenesis): Ensures that a transaction was sent for the same
//! network. Determined based on genesis.
//!
//! - [`CheckMortality`](frame_system::CheckMortality): Extends a transaction with a configurable
//! mortality.
//!
//! - [`CheckNonZeroSender`](frame_system::CheckNonZeroSender): Ensures that the sender of a
//! transaction is not the *all zero account* (all bytes of the accountid are zero).
//!
//! - [`CheckNonce`](frame_system::CheckNonce): Extends a transaction with a nonce to prevent replay
//! of transactions and to provide ordering of transactions.
//!
//! - [`CheckSpecVersion`](frame_system::CheckSpecVersion): Ensures that a transaction was built for
//! the currently active runtime.
//!
//! - [`CheckTxVersion`](frame_system::CheckTxVersion): Ensures that the transaction signer used the
//! correct encoding of the call.
//!
//! - [`CheckWeight`](frame_system::CheckWeight): Ensures that the transaction fits into the block
//! before dispatching it.
//!
//! - [`ChargeTransactionPayment`](pallet_transaction_payment::ChargeTransactionPayment): Charges
//! transaction fees from the signer based on the weight of the call using the native token.
//!
//! - [`ChargeAssetTxPayment`](pallet_asset_tx_payment::ChargeAssetTxPayment): Charges transaction
//! fees from the signer based on the weight of the call using any supported asset (including the
//! native token).
//!
//! - [`ChargeAssetTxPayment`(using
//! conversion)](pallet_asset_conversion_tx_payment::ChargeAssetTxPayment): Charges transaction
//! fees from the signer based on the weight of the call using any supported asset (including the
//! native token). The asset is converted to the native token using a pool.
//!
//! - [`SkipCheckIfFeeless`](pallet_skip_feeless_payment::SkipCheckIfFeeless): Allows transactions
//! to be processed without paying any fee. This requires that the `call` that should be
//! dispatched is augmented with the [`feeless_if`](frame_support::pallet_macros::feeless_if)
//! attribute.
//!
//! - [`CheckMetadataHash`](frame_metadata_hash_extension::CheckMetadataHash): Extends transactions
//! to include the so-called metadata hash. This is required by chains to support the generic
//! Ledger application and other similar offline wallets.
//!
//! - [`WeightReclaim`](frame_system::WeightReclaim): A transaction extension for the relay chain
//! that reclaims unused weight after executing a transaction.
//!
//! - [`StorageWeightReclaim`](cumulus_pallet_weight_reclaim::StorageWeightReclaim): A transaction
//! extension for teyrchains that reclaims unused storage weight after executing a transaction.
//!
//! For more information about these extensions, follow the link to the type documentation.
//!
//! # Building a custom transaction extension
//!
//! Defining a couple of very simple transaction extensions looks like the following:
#![doc = docify::embed!("./src/reference_docs/transaction_extensions.rs", transaction_extensions_example)]
#[docify::export]
pub mod transaction_extensions_example {
use codec::{Decode, DecodeWithMemTracking, Encode};
use scale_info::TypeInfo;
use sp_runtime::{
impl_tx_ext_default,
traits::{Dispatchable, TransactionExtension},
transaction_validity::TransactionValidityError,
};
// This doesn't actually check anything, but simply allows
// some arbitrary `u32` to be added to the extrinsic payload
#[derive(Debug, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
pub struct AddToPayload(pub u32);
impl<Call: Dispatchable> TransactionExtension<Call> for AddToPayload {
const IDENTIFIER: &'static str = "AddToPayload";
type Implicit = ();
type Pre = ();
type Val = ();
impl_tx_ext_default!(Call; weight validate prepare);
}
// This is the opposite; nothing will be added to the extrinsic payload,
// but the Implicit type (`1234u32`) will be added to the
// payload to be signed.
#[derive(Debug, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
pub struct AddToSignaturePayload;
impl<Call: Dispatchable> TransactionExtension<Call> for AddToSignaturePayload {
const IDENTIFIER: &'static str = "AddToSignaturePayload";
type Implicit = u32;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
Ok(1234)
}
type Pre = ();
type Val = ();
impl_tx_ext_default!(Call; weight validate prepare);
}
}
@@ -0,0 +1,91 @@
//! # Umbrella Crate
//!
//! The Pezkuwi-SDK "umbrella" is a crate that re-exports all other published crates. This makes it
//! possible to have a very small `Cargo.toml` file that only has one dependency, the umbrella
//! crate. This helps with selecting the right combination of crate versions, since otherwise 3rd
//! party tools are needed to select a compatible set of versions.
//!
//!
//! ## Features
//!
//! The umbrella crate supports no-std builds and can therefore be used in the runtime and node.
//! There are two main features: `runtime` and `node`. The `runtime` feature enables all `no-std`
//! crates, while the `node` feature enables all `std` crates. It should be used like any other
//! crate in the repo, with `default-features = false`.
//!
//! For more fine-grained control, additionally, each crate can be enabled selectively. The umbrella
//! exposes one feature per dependency. For example, if you only want to use the `frame-support`
//! crate, you can enable the `frame-support` feature.
//!
//! The umbrella exposes a few more general features:
//! - `tuples-96`: Needs to be enabled for runtimes that have more than 64 pallets.
//! - `serde`: Specifically enable `serde` en/decoding support.
//! - `experimental`: Experimental enable experimental features - should not yet used in production.
//! - `with-tracing`: Enable tracing support.
//! - `try-runtime`, `runtime-benchmarks` and `std`: These follow the standard conventions.
//! - `runtime`: As described above, enable all `no-std` crates.
//! - `node`: As described above, enable all `std` crates.
//! - There does *not* exist a dedicated docs feature. To generate docs, enable the `runtime` and
//! `node` feature. For `docs.rs` the manifest contains specific configuration to make it show up
//! all re-exports.
//!
//! There is a specific [`zepter`](https://github.com/ggwpez/zepter) check in place to ensure that
//! the features of the umbrella are correctly configured. This check is run in CI and locally when
//! running `zepter`.
//!
//! ## Generation
//!
//! The umbrella crate needs to be updated every time when a new crate is added or removed from the
//! workspace. It is checked in CI by calling its generation script. The generation script is
//! located in `./scripts/generate-umbrella.py` and needs dependency `cargo_workspace`.
//!
//! Example: `python3 scripts/generate-umbrella.py --sdk . --version 1.9.0`
//!
//! ## Usage
//!
//! > Note: You can see a live example in the `staging-node-cli` and `kitchensink-runtime` crates.
//!
//! The umbrella crate can be added to your runtime crate like this:
//!
//! `pezkuwi-sdk = { path = "../../../../umbrella", features = ["runtime"], default-features =
//! false }`
//!
//! or for a node:
//!
//! `pezkuwi-sdk = { path = "../../../../umbrella", features = ["node"], default-features = false
//! }`
//!
//! In the code, it is then possible to bring all dependencies into scope via:
//!
//! `use pezkuwi_sdk::*;`
//!
//! ### Known Issues
//!
//! The only known issue so far is the fact that the `use` statement brings the dependencies only
//! into the outer module scope - not the global crate scope. For example, the following code would
//! need to be adjusted:
//!
//! ```rust
//! use pezkuwi_sdk::*;
//!
//! mod foo {
//! // This does sadly not compile:
//! frame_support::parameter_types! { }
//!
//! // Instead, we need to do this (or add an equivalent `use` statement):
//! pezkuwi_sdk::frame_support::parameter_types! { }
//! }
//! ```
//!
//! Apart from this, no issues are known. There could be some bugs with how macros locate their own
//! re-exports. Please [report issues](https://github.com/pezkuwichain/pezkuwi-sdk/issues) that arise from using this crate.
//!
//! ## Dependencies
//!
//! The umbrella crate re-exports all published crates, with a few exceptions:
//! - Runtime crates like `pezkuwichain-runtime` etc are not exported. This otherwise leads to very
//! weird compile errors and should not be needed anyway.
//! - Example and fuzzing crates are not exported. This is currently detected by checking the name
//! of the crate for these magic words. In the future, it will utilize custom metadata, as it is
//! done in the `pezkuwichain-runtime` crate.
//! - The umbrella crate itself. Should be obvious :)
@@ -0,0 +1,158 @@
//! # WASM Meta Protocol
//!
//! All Substrate based chains adhere to a unique architectural design novel to the Pezkuwi
//! ecosystem. We refer to this design as the "**WASM Meta Protocol**".
//!
//! Consider the fact that a traditional blockchain software is usually a monolithic artifact.
//! **Upgrading any part of the system implies upgrading the entire system**. This has historically
//! led to cumbersome forkful upgrades to be the status quo in blockchain ecosystems. In other
//! words, the entire node software is the specification of the blockchain's [`state transition
//! function`](crate::reference_docs::blockchain_state_machines).
//!
//! Moreover, the idea of "storing code in the state" is explored in the context of smart contracts
//! platforms, but has not been expanded further.
//!
//! Substrate mixes these two ideas together, and takes the novel approach of storing the
//! blockchain's main "state transition function" in the main blockchain state, in the same fashion
//! that a smart contract platform stores the code of individual contracts in its state. As noted in
//! [`crate::reference_docs::blockchain_state_machines`], this state transition function is called
//! the **Runtime**, and WASM is chosen as the bytecode. The Runtime is stored under a special key
//! in the state (see [`sp_core::storage::well_known_keys`]) and can be updated as a part of the
//! state transition function's execution, just like a user's account balance can be updated.
//!
//! > Note that while we drew an analogy between smart contracts and runtimes in the above, there
//! > are fundamental differences between the two, explained in
//! > [`crate::reference_docs::runtime_vs_smart_contract`].
//!
//! The rest of the system that is NOT the state transition function is called the
//! [**Node**](crate::reference_docs::glossary#node), and is a normal binary that is compiled from
//! Rust to different hardware targets.
//!
//! This design enables all Substrate-based chains to be fork-less-ly upgradeable, because the
//! Runtime can be updated on the fly, within the execution of a block, and the node is (for the
//! most part) oblivious to the change that is happening.
//!
//! Therefore, the high-level architecture of a any Substrate-based chain can be demonstrated as
//! follows:
#![doc = simple_mermaid::mermaid!("../../../mermaid/substrate_simple.mmd")]
//!
//! The node and the runtime need to communicate. This is done through two concepts:
//!
//! 1. **Host functions**: a way for the (WASM) runtime to talk to the node. All host functions are
//! defined in [`sp_io`]. For example, [`sp_io::storage`] are the set of host functions that
//! allow the runtime to read and write data to the on-chain state.
//! 2. **Runtime APIs**: a way for the node to talk to the WASM runtime. Runtime APIs are defined
//! using macros and utilities in [`sp_api`]. For example, [`sp_api::Core`] is the most
//! fundamental runtime API that any blockchain must implement in order to be able to (re)
//! execute blocks.
#![doc = simple_mermaid::mermaid!("../../../mermaid/substrate_client_runtime.mmd")]
//!
//! A runtime must have a set of runtime APIs in order to have any meaningful blockchain
//! functionality, but it can also expose more APIs. See
//! [`crate::reference_docs::custom_runtime_api_rpc`] as an example of how to add custom runtime
//! APIs to your FRAME-based runtime.
//!
//! Similarly, for a runtime to be "compatible" with a node, the node must implement the full set of
//! host functions that the runtime at any point in time requires. Given the fact that a runtime can
//! evolve in time, and a blockchain node (typically) wishes to be capable of re-executing all the
//! previous blocks, this means that a node must always maintain support for the old host functions.
//! **This implies that adding a new host function is a big commitment and should be done with
//! care**. This is why, for example, adding a new host function to Pezkuwi always requires an RFC.
//! Learn how to add a new host function to your runtime in
//! [`crate::reference_docs::custom_host_functions`].
//!
//! ## Node vs. Runtime
//!
//! A common question is: which components of the system end up being part of the node, and which
//! ones of the runtime?
//!
//! Recall from [`crate::reference_docs::blockchain_state_machines`] that the runtime is the state
//! transition function. Anything that needs to influence how your blockchain's state is updated,
//! should be a part of the runtime. For example, the logic around currency, governance, identity or
//! any other application-specific logic that has to do with the state is part of the runtime.
//!
//! Anything that does not have to do with the state-transition function and will only
//! facilitate/enable it is part of the node. For example, the database, networking, and even
//! consensus algorithm are all node-side components.
//!
//! > The consensus is to your runtime what HTTP is to a web-application. It is the underlying
//! > engine that enables trustless execution of the runtime in a distributed manner whilst
//! > maintaining a canonical outcome of that execution.
#![doc = simple_mermaid::mermaid!("../../../mermaid/substrate_with_frame.mmd")]
//!
//! ## State
//!
//! From the previous sections, we know that the database component is part of the node, not the
//! runtime. We also hinted that a set of host functions ([`sp_io::storage`]) are how the runtime
//! issues commands to the node to read/write to the state. Let's dive deeper into this.
//!
//! The state of the blockchain, what we seek to come to consensus about, is indeed *kept* in the
//! node side. Nonetheless, the runtime is the only component that:
//!
//! 1. Can update the state.
//! 2. Can fully interpret the state.
//!
//! In fact, [`sp_core::storage::well_known_keys`] are the only state keys that the node side is
//! aware of. The rest of the state, including what logic the runtime has, what balance each user
//! has and such, are all only comprehensible to the runtime.
#![doc = simple_mermaid::mermaid!("../../../mermaid/state.mmd")]
//!
//! In the above diagram, all of the state keys and values are opaque bytes to the node. The node
//! does not know what they mean, and it does not know what is the type of the corresponding value
//! (e.g. if it is a number of a vector). Contrary, the runtime knows both the meaning of their
//! keys, and the type of the values.
//!
//! This opaque-ness is the fundamental reason why Substrate-based chains can fork-less-ly upgrade:
//! because the node side code is kept oblivious to all of the details of the state transition
//! function. Therefore, the state transition function can freely upgrade without the node needing
//! to know.
//!
//! ## Native Runtime
//!
//! Historically, the node software also kept a native copy of the runtime at the time of
//! compilation within it. This used to be called the "Native Runtime". The main purpose of the
//! native runtime used to be leveraging the faster execution time and better debugging
//! infrastructure of native code. However, neither of the two arguments strongly hold and the
//! native runtime is being fully removed from the node-sdk.
//!
//! See: <https://github.com/pezkuwichain/pezkuwi-sdk/issues/97>
//!
//! > Also, note that the flags [`sc_cli::ExecutionStrategy::Native`] is already a noop and all
//! > chains built with Substrate only use WASM execution.
//!
//! ### Runtime Versions
//!
//! An important detail of the native execution worth learning about is that the node software,
//! obviously, only uses the native runtime if it is the same code as with the wasm blob stored
//! onchain. Else, nodes who run the native runtime will come to a different state transition. How
//! do nodes determine if two runtimes are the same? Through the very important
//! [`sp_version::RuntimeVersion`]. All runtimes expose their version via a runtime api
//! ([`sp_api::Core::version`]) that returns this struct. The node software, or other applications,
//! inspect this struct to examine the identity of a runtime, and to determine if two runtimes are
//! the same. Namely, [`sp_version::RuntimeVersion::spec_version`] is the main key that implies two
//! runtimes are the same.
//!
//! Therefore, it is utmost important to make sure before any runtime upgrade, the spec version is
//! updated.
//!
//! ## Example: Block Execution.
//!
//! As a final example to recap, let's look at how Substrate-based nodes execute blocks. Blocks are
//! received in the node side software as opaque blobs and in the networking layer.
//!
//! At some point, based on the consensus algorithm's rules, the node decides to import (aka.
//! *validate*) a block.
//!
//! * First, the node will fetch the state of the parent hash of the block that wishes to be
//! imported.
//! * The runtime is fetched from this state, and placed into a WASM execution environment.
//! * The [`sp_api::Core::execute_block`] runtime API is called and the block is passed in as an
//! argument.
//! * The runtime will then execute the block, and update the state accordingly. Any state update is
//! issued via the [`sp_io::storage`] host functions.
//! * Both the runtime and node will check the state-root of the state after the block execution to
//! match the one claimed in the block header.
//!
//! > Example taken from [this
//! > lecture](https://www.youtube.com/watch?v=v0cKuddbF_Q&list=PL-w_i5kwVqbkRmfDn5nzeuU1S_FFW8dDg&index=4)
//! > of the Pezkuwi Blockchain Academy.