mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 00:37:57 +00:00
Move developer-hub to polkadot-sdk-docs (#2598)
This PR is a continuation of https://github.com/paritytech/polkadot-sdk/pull/2102 and part of an initiative started here https://hackmd.io/@romanp/rJ318ZCEp What has been done: - The content under `docs/*` (with the exception of `docs/mermaid`) has been moved to `docs/contributor/` - Developer Hub has been renamed to Polkadot SDK Docs, and the crate has been renamed from `developer-hub` to `polkadot-sdk-docs` - The content under `developer-hub/*` has been moved to `docs/sdk` --- Original PR https://github.com/paritytech/polkadot-sdk/pull/2565, it has been close due to too many rebase conflicts --------- Co-authored-by: Serban Iorga <serban@parity.io> Co-authored-by: Chevdor <chevdor@users.noreply.github.com> Co-authored-by: Egor_P <egor@parity.io> Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
//! # Changing Consensus
|
||||
@@ -0,0 +1 @@
|
||||
//! # Cumulus Enabled Parachain
|
||||
@@ -0,0 +1,25 @@
|
||||
//! # Polkadot SDK Docs Guides
|
||||
//!
|
||||
//! This crate contains a collection of guides that are foundational to the developers of
|
||||
//! Polkadot SDK. They common user-journeys that are traversed in the Polkadot ecosystem.
|
||||
|
||||
/// Write your first simple pallet, learning the most most basic features of FRAME along the way.
|
||||
pub mod your_first_pallet;
|
||||
|
||||
/// Writing your first real [runtime](`crate::reference_docs::wasm_meta_protocol`), and successfully
|
||||
/// compiling it to [WASM](crate::polkadot_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 change the consensus engine of both the node and the runtime.
|
||||
pub mod changing_consensus;
|
||||
|
||||
/// How to enhance a given runtime and node to be cumulus-enabled, run it as a parachain and connect
|
||||
/// it to a relay-chain.
|
||||
pub mod cumulus_enabled_parachain;
|
||||
|
||||
/// How to make a given runtime XCM-enabled, capable of sending messages (`Transact`) between itself
|
||||
/// and the relay chain to which it is connected.
|
||||
pub mod xcm_enabled_parachain;
|
||||
@@ -0,0 +1 @@
|
||||
//! # XCM Enabled Parachain
|
||||
@@ -0,0 +1 @@
|
||||
//! # Your first Node
|
||||
@@ -0,0 +1,738 @@
|
||||
//! # Currency Pallet
|
||||
//!
|
||||
//! By the end of this guide, you will write a small FRAME pallet (see
|
||||
//! [`crate::polkadot_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 advance FRAME related topics are discussed in [`crate::reference_docs`].
|
||||
//!
|
||||
//! ## Topics Covered
|
||||
//!
|
||||
//! The following FRAME topics are covered in this guide. See the documentation of the
|
||||
//! associated items to know more.
|
||||
//!
|
||||
//! - [Storage](frame::pallet_macros::storage)
|
||||
//! - [Call](frame::pallet_macros::call)
|
||||
//! - [Event](frame::pallet_macros::event)
|
||||
//! - [Error](frame::pallet_macros::error)
|
||||
//! - Basics of testing a pallet
|
||||
//! - [Constructing a runtime](frame::runtime::prelude::construct_runtime)
|
||||
//!
|
||||
//! ## Writing Your First Pallet
|
||||
//!
|
||||
//! 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::polkadot_sdk::frame_runtime`]
|
||||
//!
|
||||
//! ### Shell Pallet
|
||||
//!
|
||||
//! Consider the following as a "shell pallet". We continue building the rest of this pallet based
|
||||
//! on this template.
|
||||
//!
|
||||
//! [`pallet::config`](frame::pallet_macros::config) and
|
||||
//! [`pallet::pallet`](frame::pallet_macros::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!("./src/guides/your_first_pallet/mod.rs", shell_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.
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Balance)]
|
||||
//!
|
||||
//! The definition of these two storage items, based on [`frame::pallet_macros::storage`] details,
|
||||
//! is as follows:
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", TotalIssuance)]
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Balances)]
|
||||
//!
|
||||
//! ### Dispatchables
|
||||
//!
|
||||
//! Next, we will define the dispatchable functions. As per [`frame::pallet_macros::call`], these
|
||||
//! will be defined as normal `fn`s attached to `struct Pallet`.
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", impl_pallet)]
|
||||
//!
|
||||
//! The logic of the 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 `who`.
|
||||
#![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<%26'static+str>-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
|
||||
//! [`frame::pallet_macros::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!("./src/guides/your_first_pallet/mod.rs", transfer_better)]
|
||||
//!
|
||||
//! Moreover, you will learn in the [Safe Defensive Programming
|
||||
//! section](crate::reference_docs::safe_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!("./src/guides/your_first_pallet/mod.rs", transfer_better_checked)]
|
||||
//!
|
||||
//! This is more or less all the logic that there is this basic currency pallet!
|
||||
//!
|
||||
//! ### Your First (Test) Runtime
|
||||
//!
|
||||
//! Next, we create a "test runtime" in order to test our pallet. Recall from
|
||||
//! [`crate::polkadot_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!("./src/guides/your_first_pallet/mod.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!("./src/guides/your_first_pallet/mod.rs", first_test)]
|
||||
//!
|
||||
//! In the first test, we simply assert that there is no total issuance, and no balance associated
|
||||
//! with account `1`. Then, we mint some balance into `1`, 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.
|
||||
//!
|
||||
//! TODO: hard to explain exactly `RuntimeOrigin::signed(1)` at this point.
|
||||
//!
|
||||
//! 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 Polkadot Blockchain Academy's Rust entrance exam has a
|
||||
//! > [section](https://github.com/Polkadot-Blockchain-Academy/pba-qualifier-exam/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!("./src/guides/your_first_pallet/mod.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![(1, 100), (2, 100)];` so
|
||||
//! far. Then, if desired, we attach a default value for this struct.
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", default_state_builder)]
|
||||
//!
|
||||
//! Like any other builder pattern, we attach functions to the type to mutate its internal
|
||||
//! properties.
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.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!("./src/guides/your_first_pallet/mod.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!("./src/guides/your_first_pallet/mod.rs", state_builder_works)]
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.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!("./src/guides/your_first_pallet/mod.rs", transfer_works)]
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.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!("./src/guides/your_first_pallet/mod.rs", transfer_from_non_existent_fails)]
|
||||
//!
|
||||
//! We leave it up to you to write a test that triggers to `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. 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 [`frame::pallet_macros::error`].
|
||||
//!
|
||||
//! - **Event**: Events are akin to the return type of dispatchables. 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.
|
||||
//!
|
||||
//! TODO: both need to be improved a lot at the pallet-macro rust-doc level. Also my explanation
|
||||
//! of event is probably not the best.
|
||||
//!
|
||||
//! 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 an extra `#[frame::event/error]` attribute
|
||||
//! attached.
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Event)]
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", Error)]
|
||||
//!
|
||||
//! One slightly custom part of this is the `#[pallet::generate_deposit(pub(super) fn
|
||||
//! deposit_event)]` 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!("./src/guides/your_first_pallet/mod.rs", config_v2)]
|
||||
//!
|
||||
//! > These `Runtime*` types are better explained in
|
||||
//! > [`crate::reference_docs::frame_composite_enums`].
|
||||
//!
|
||||
//! Then, we can rewrite the `transfer` dispatchable as such:
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_v2)]
|
||||
//!
|
||||
//! Then, notice how now we would need to provide this `type RuntimeEvent` in our test runtime
|
||||
//! setup.
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", runtime_v2)]
|
||||
//!
|
||||
//! In this snippet, the actual `RuntimeEvent` type (right hand side of `type RuntimeEvent =
|
||||
//! RuntimeEvent`) is generated by `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::safe_defensive_programming`].
|
||||
//! - [`crate::reference_docs::frame_origin`].
|
||||
//! - [`crate::reference_docs::frame_composite_enums`].
|
||||
//! - The pallet we wrote in this guide was using `dev_mode`, learn more in
|
||||
//! [`frame::pallet_macros::config`].
|
||||
//! - Learn more about the individual pallet items/macros, such as event and errors and call, in
|
||||
//! [`frame::pallet_macros`].
|
||||
|
||||
#[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 reminder = sender_balance - amount;
|
||||
|
||||
// update sender and dest balances.
|
||||
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||
Balances::<T>::insert(&sender, reminder);
|
||||
|
||||
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 reminder = 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 reminder = 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::*;
|
||||
use frame::testing_prelude::*;
|
||||
|
||||
#[docify::export]
|
||||
mod runtime {
|
||||
use super::*;
|
||||
// we need to reference our `mod pallet` as an identifier to pass to
|
||||
// `construct_runtime`.
|
||||
use crate::guides::your_first_pallet::pallet as pallet_currency;
|
||||
|
||||
construct_runtime!(
|
||||
pub struct Runtime {
|
||||
// ---^^^^^^ This is where `struct Runtime` is defined.
|
||||
System: frame_system,
|
||||
Currency: pallet_currency,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||
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![(1, 100), (2, 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![(1, 100), (2, 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 account 1 to have no funds.
|
||||
assert_eq!(Balances::<Runtime>::get(&1), None);
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), None);
|
||||
|
||||
// mint some funds into 1
|
||||
assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(1), 1, 100));
|
||||
|
||||
// re-check the above
|
||||
assert_eq!(Balances::<Runtime>::get(&1), 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(&1), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&2), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&3), None);
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||
});
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn state_builder_add_balance() {
|
||||
StateBuilder::default().add_balance(3, 42).build_and_execute(|| {
|
||||
assert_eq!(Balances::<Runtime>::get(&3), Some(42));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(242));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn state_builder_duplicate_genesis_fails() {
|
||||
StateBuilder::default()
|
||||
.add_balance(3, 42)
|
||||
.add_balance(3, 43)
|
||||
.build_and_execute(|| {
|
||||
assert_eq!(Balances::<Runtime>::get(&3), 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(1), 2, 100));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&2), Some(200));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(300));
|
||||
|
||||
// given:
|
||||
assert_ok!(Pallet::<Runtime>::mint_unsafe(RuntimeOrigin::signed(1), 3, 100));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&3), Some(100));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(400));
|
||||
});
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn transfer_works() {
|
||||
StateBuilder::default().build_and_execute(|| {
|
||||
// given the the initial state, when:
|
||||
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(1), 2, 50));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&1), Some(50));
|
||||
assert_eq!(Balances::<Runtime>::get(&2), Some(150));
|
||||
assert_eq!(TotalIssuance::<Runtime>::get(), Some(200));
|
||||
|
||||
// when:
|
||||
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(2), 1, 50));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&1), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&2), 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 the initial state, when:
|
||||
assert_err!(
|
||||
Pallet::<Runtime>::transfer(RuntimeOrigin::signed(3), 1, 10),
|
||||
"NonExistentAccount"
|
||||
);
|
||||
|
||||
// then nothing has changed.
|
||||
assert_eq!(Balances::<Runtime>::get(&1), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&2), Some(100));
|
||||
assert_eq!(Balances::<Runtime>::get(&3), 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.
|
||||
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 reminder =
|
||||
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, reminder);
|
||||
|
||||
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::*;
|
||||
|
||||
#[docify::export]
|
||||
pub mod runtime_v2 {
|
||||
use super::*;
|
||||
use crate::guides::your_first_pallet::pallet_v2 as pallet_currency;
|
||||
|
||||
construct_runtime!(
|
||||
pub struct Runtime {
|
||||
System: frame_system,
|
||||
Currency: pallet_currency,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||
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(1);
|
||||
|
||||
// given the the initial state, when:
|
||||
assert_ok!(Pallet::<Runtime>::transfer(RuntimeOrigin::signed(1), 2, 50));
|
||||
|
||||
// then:
|
||||
assert_eq!(Balances::<Runtime>::get(&1), Some(50));
|
||||
assert_eq!(Balances::<Runtime>::get(&2), 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: 1, to: 2, amount: 50 }]
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#[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("NotEnoughBalance".into())
|
||||
}
|
||||
let reminder = sender_balance - amount;
|
||||
|
||||
// update sender and dest balances.
|
||||
Balances::<T>::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount));
|
||||
Balances::<T>::insert(&sender, reminder);
|
||||
|
||||
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, "NotEnoughBalance");
|
||||
let reminder = 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 reminder = sender_balance.checked_sub(amount).ok_or("NotEnoughBalance")?;
|
||||
|
||||
// .. snip
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//! # Your first Runtime
|
||||
@@ -0,0 +1,43 @@
|
||||
//! # Polkadot SDK Docs
|
||||
//!
|
||||
//! The Polkadot SDK Developer Documentation.
|
||||
//!
|
||||
//! This crate is a *minimal*, but *always-accurate* source of information for those wishing to
|
||||
//! build on the Polkadot SDK.
|
||||
//!
|
||||
//! > **Work in Progress**: This crate is under heavy development. Expect content to be moved and
|
||||
//! > changed. Do not use links to this crate yet. See [`meta_contributing`] for more information.
|
||||
//!
|
||||
//! ## Getting Started
|
||||
//!
|
||||
//! We suggest the following reading sequence:
|
||||
//!
|
||||
//! - Start by learning about the the [`polkadot_sdk`], its structure and context.
|
||||
//! - Then, head over the [`guides`]. This modules contains in-depth guides about the most important
|
||||
//! user-journeys of the Polkadot SDK.
|
||||
//! - Whilst reading the guides, you might find back-links to [`crate::reference_docs`].
|
||||
//! - Finally, <https://paritytech.github.io> is the parent website of this crate that contains the
|
||||
//! list of further tools related to the Polkadot SDK.
|
||||
//!
|
||||
//! ## Information Architecture
|
||||
//!
|
||||
//! This section paints a picture over the high-level information architecture of this crate.
|
||||
#![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")]
|
||||
#![allow(rustdoc::invalid_html_tags)] // TODO: remove later. https://github.com/paritytech/polkadot-sdk-docs/issues/65
|
||||
#![allow(rustdoc::bare_urls)] // TODO: remove later. https://github.com/paritytech/polkadot-sdk-docs/issues/65
|
||||
#![warn(rustdoc::broken_intra_doc_links)]
|
||||
#![warn(rustdoc::private_intra_doc_links)]
|
||||
|
||||
/// 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;
|
||||
|
||||
/// In-depth guides about the most common components of the Polkadot SDK. They are slightly more
|
||||
/// high level and broad than reference docs.
|
||||
pub mod guides;
|
||||
/// An introduction to the Polkadot 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 polkadot_sdk;
|
||||
/// Reference documents covering in-depth topics across the Polkadot 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,146 @@
|
||||
//! # 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 Polkadot 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 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 Polkadot 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/paritytech/polkadot-sdk/issues/991>.
|
||||
//!
|
||||
//! ## 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 layed 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](#crate::why-rust-docs), the more concise this
|
||||
//! crate is, the better.
|
||||
//! 3. √ Don’t 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 about documenting guidelines, see:
|
||||
//! <https://github.com/paritytech/polkadot-sdk/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_composite_enums`]. 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>
|
||||
//!
|
||||
//! ## `docs.substrate.io`
|
||||
//!
|
||||
//! This crate is meant to gradually replace `docs.substrate.io`. As any content is added here, the
|
||||
//! corresponding counter-part should be marked as deprecated, as described
|
||||
//! [here](https://github.com/paritytech/polkadot-sdk-docs/issues/26).
|
||||
//!
|
||||
//! ## `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.
|
||||
//!
|
||||
//! ## How to Build
|
||||
//!
|
||||
//! To build this crate properly, with with right HTML headers injected, run:
|
||||
//!
|
||||
//! ```no_compile
|
||||
//! RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/toc.html" cargo doc -p polkadot-sdk-docs
|
||||
//! ```
|
||||
//!
|
||||
//! adding `--no-deps` would speed up the process while development. 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.
|
||||
@@ -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 Polkadot parachains.
|
||||
//!
|
||||
//! > 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 parachain 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_parachain_system`]
|
||||
//! - [`parachain_info`]
|
||||
#![doc = docify::embed!("./src/polkadot_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/polkadot_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 parachain runtime's validation runtime API, also known as
|
||||
//! parachain validation function (PVF). Without this API, the relay chain is unable to validate
|
||||
//! blocks produced by our parachain.
|
||||
#![doc = docify::embed!("./src/polkadot_sdk/cumulus.rs", validate_block)]
|
||||
//!
|
||||
//! ---
|
||||
//!
|
||||
//! [FRAME]: crate::polkadot_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 struct Runtime {
|
||||
// system-level pallets.
|
||||
System: frame_system,
|
||||
Timestamp: pallet_timestamp,
|
||||
ParachainSystem: cumulus_pallet_parachain_system,
|
||||
ParachainInfo: parachain_info,
|
||||
|
||||
// parachain 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 as frame_system::DefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
type Block = MockBlock<Self>;
|
||||
type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;
|
||||
}
|
||||
|
||||
impl cumulus_pallet_parachain_system::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type OnSystemEvent = ();
|
||||
type SelfParaId = parachain_info::Pallet<Runtime>;
|
||||
type OutboundXcmpMessageSource = ();
|
||||
type XcmpMessageHandler = ();
|
||||
type ReservedDmpWeight = ();
|
||||
type ReservedXcmpWeight = ();
|
||||
type CheckAssociatedRelayNumber =
|
||||
cumulus_pallet_parachain_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>>;
|
||||
}
|
||||
|
||||
impl parachain_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>;
|
||||
#[cfg(feature = "experimental")]
|
||||
type SlotDuration = pallet_aura::MinimumPeriodTimesTwo<Self>;
|
||||
}
|
||||
|
||||
#[docify::export(timestamp)]
|
||||
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig as pallet_timestamp::DefaultConfig)]
|
||||
impl pallet_timestamp::Config for Runtime {}
|
||||
|
||||
impl cumulus_pallet_aura_ext::Config for Runtime {}
|
||||
}
|
||||
|
||||
#[docify::export(validate_block)]
|
||||
cumulus_pallet_parachain_system::register_validate_block! {
|
||||
Runtime = Runtime,
|
||||
BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
//! # 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 examples showcases a minimal pallet.
|
||||
#![doc = docify::embed!("src/polkadot_sdk/frame_runtime.rs", pallet)]
|
||||
//!
|
||||
//!
|
||||
//! 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/polkadot_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).
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use frame::prelude::*;
|
||||
|
||||
/// 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 super::*;
|
||||
|
||||
/// 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.
|
||||
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 tha 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/root
|
||||
/// 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 `struct Runtime`
|
||||
construct_runtime!(
|
||||
pub struct 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 as frame_system::DefaultConfig)]
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
//! # Polkadot SDK
|
||||
//!
|
||||
//! [Polkadot SDK](https://github.com/paritytech/polkadot-sdk) provides the main resources needed to
|
||||
//! start building on the [Polkadot network](https://polkadot.network), a scalable, multi-chain
|
||||
//! blockchain platform that enables different blockchains to securely interoperate.
|
||||
//!
|
||||
//! [](https://substrate.stackexchange.com/)
|
||||
//!
|
||||
//! [](https://github.com/Awsmdot/awesome-dot)
|
||||
//! [](https://wiki.polkadot.network/)
|
||||
//! [](https://forum.polkadot.network/)
|
||||
//!
|
||||
//! [](https://github.com/polkadot-fellows/rfcs)
|
||||
//! [](https://github.com/polkadot-fellows/runtimes)
|
||||
//! [](https://github.com/polkadot-fellows/manifesto)
|
||||
//!
|
||||
//! ## Getting Started
|
||||
//!
|
||||
//! The primary way to get started with the Polkadot SDK is to start writing a FRAME-based runtime.
|
||||
//! See:
|
||||
//!
|
||||
//! * [`polkadot`], to understand what is Polkadot as a development platform.
|
||||
//! * [`substrate`], for an overview of what Substrate as the main blockchain framework of Polkadot
|
||||
//! SDK.
|
||||
//! * [`frame`], to learn about how to write blockchain applications aka. "App Chains".
|
||||
//! * Continue with the [`polkadot_sdk_docs`'s "getting started"](crate#getting-started).
|
||||
//!
|
||||
//! ## Components
|
||||
//!
|
||||
//! #### Substrate
|
||||
//!
|
||||
//! [](https://github.com/paritytech/polkadot-sdk/blob/master/substrate/LICENSE-APACHE2)
|
||||
//! [](https://github.com/paritytech/polkadot-sdk/blob/master/substrate)
|
||||
//!
|
||||
//! [`substrate`] is the base blockchain framework used to power the Polkadot SDK. It is a full
|
||||
//! toolkit to create sovereign blockchains, including but not limited to those who connect to
|
||||
//! Polkadot as parachains.
|
||||
//!
|
||||
//! #### FRAME
|
||||
//!
|
||||
//! [](https://github.com/paritytech/polkadot-sdk/blob/master/substrate/LICENSE-APACHE2)
|
||||
//! [](https://github.com/paritytech/polkadot-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
|
||||
//!
|
||||
//! [](https://github.com/paritytech/polkadot-sdk/blob/master/cumulus/LICENSE)
|
||||
//! [](https://github.com/paritytech/polkadot-sdk/blob/master/cumulus)
|
||||
//!
|
||||
//! [`cumulus`] transforms FRAME-based runtimes into Polkadot-compatible parachain runtimes, and
|
||||
//! Substrate-based nodes into Polkadot/Parachain-compatible nodes.
|
||||
//!
|
||||
//! #### XCM
|
||||
//!
|
||||
//! [](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/LICENSE)
|
||||
//! [](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 parachains, but is intended to be extensible to other use cases as well.
|
||||
//!
|
||||
//! #### Polkadot
|
||||
//!
|
||||
//! [](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/LICENSE)
|
||||
//! [](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot)
|
||||
//!
|
||||
//! [`polkadot`] is an implementation of a Polkadot node in Rust, by `@paritytech`. The Polkadot
|
||||
//! runtimes are located under the
|
||||
//! [`polkadot-fellows/runtimes`](https://github.com/polkadot-fellows/runtimes) repository.
|
||||
//!
|
||||
//! ### Summary
|
||||
//!
|
||||
//! The following diagram summarizes how some of the components of Polkadot SDK work together:
|
||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/polkadot_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 [`crate::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/polkadot_sdk_polkadot.mmd")]
|
||||
//! Polkadot 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 parachain is a "special" Substrate-based chain, whereby both the node and the runtime
|
||||
//! components have became "Polkadot-aware" using Cumulus.
|
||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/polkadot_sdk_parachain.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 parts of the
|
||||
//! Polkadot SDK:
|
||||
//!
|
||||
//! * [Polygon's spin-off, 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)
|
||||
//!
|
||||
//! [`substrate`]: crate::polkadot_sdk::substrate
|
||||
//! [`frame`]: crate::polkadot_sdk::frame_runtime
|
||||
//! [`cumulus`]: crate::polkadot_sdk::cumulus
|
||||
//! [`polkadot`]: crate::polkadot_sdk::polkadot
|
||||
//! [`xcm`]: crate::polkadot_sdk::xcm
|
||||
|
||||
/// Lean about Cumulus, the framework that transforms [`substrate`]-based chains into
|
||||
/// [`polkadot`]-enabled parachains.
|
||||
pub mod cumulus;
|
||||
/// Learn about FRAME, the framework used to build Substrate runtimes.
|
||||
pub mod frame_runtime;
|
||||
/// Learn about Polkadot as a platform.
|
||||
pub mod polkadot;
|
||||
/// Learn about different ways through which smart contracts can be utilized on top of Substrate,
|
||||
/// and in the Polkadot ecosystem.
|
||||
pub mod smart_contracts;
|
||||
/// Learn about Substrate, the main blockchain framework used in the Polkadot 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,87 @@
|
||||
//! # Polkadot
|
||||
//!
|
||||
//! Implementation of the Polkadot node/host in Rust.
|
||||
//!
|
||||
//! ## Learn More and Get Involved
|
||||
//!
|
||||
//! - [Polkadot Forum](https://forum.polkadot.network/)
|
||||
//! - [Polkadot Parachains](https://parachains.info/)
|
||||
//! - [Polkadot (multi-chain) Explorer](https://subscan.io/)
|
||||
//! - Polkadot Fellowship
|
||||
//! - [Manifesto](https://github.com/polkadot-fellows/manifesto)
|
||||
//! - [Runtimes](https://github.com/polkadot-fellows/runtimes)
|
||||
//! - [RFCs](https://github.com/polkadot-fellows/rfcs)
|
||||
//! - [Polkadot Specs](spec.polkadot.network)
|
||||
//! - [The Polkadot Parachain Host Implementers' Guide](https://paritytech.github.io/polkadot-sdk/book/)
|
||||
//! - [Whitepaper](https://www.polkadot.network/whitepaper/)
|
||||
//!
|
||||
//! ## Alternative Node Implementations 🌈
|
||||
//!
|
||||
//! - [Smoldot](https://crates.io/crates/smoldot-light). Polkadot light node/client.
|
||||
//! - [KAGOME](https://github.com/qdrvm/kagome). C++ implementation of the Polkadot host.
|
||||
//! - [Gossamer](https://github.com/ChainSafe/gossamer). Golang implementation of the Polkadot host.
|
||||
//!
|
||||
//! ## Platform
|
||||
//!
|
||||
//! In this section, we examine what what platform Polkadot exactly provides to developers.
|
||||
//!
|
||||
//! ### Polkadot White Paper
|
||||
//!
|
||||
//! The original vision of Polkadot (everything in the whitepaper, which was eventually called
|
||||
//! **Polkadot 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.
|
||||
//!
|
||||
//! Polkadot'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 "Parachains".
|
||||
//!
|
||||
//! * Shared Security: The idea of shared economic security sits at the core of Polkadot. Polkadot
|
||||
//! enables different parachains* to pool their economic security from Polkadot (i.e. "*Relay
|
||||
//! Chain*").
|
||||
//! * (heterogenous) Sharded Execution: Yet, each parachain is free to have its own execution logic
|
||||
//! (runtime), which also encompasses governance and sovereignty. Moreover, Polkadot ensures the
|
||||
//! correct execution of all parachain, without having all of its validators re-execute all
|
||||
//! parachain blocks. When seen from this perspective, the fact that Polkadot executes different
|
||||
//! parachains means it is a platform that has fully delivered (the holy grail of) "Full Execution
|
||||
//! Sharding". TODO: link to approval checking article. https://github.com/paritytech/polkadot-sdk-docs/issues/66
|
||||
//! * A framework to build blockchains: In order to materialize the ecosystem of parachains, an easy
|
||||
//! blockchain framework must exist. This is [Substrate](crate::polkadot_sdk::substrate),
|
||||
//! [FRAME](crate::polkadot_sdk::frame_runtime) and [Cumulus](crate::polkadot_sdk::cumulus).
|
||||
//! * A communication language between blockchains: In order for these blockchains to communicate,
|
||||
//! they need a shared language. [XCM](crate::polkadot_sdk::xcm) is one such language, and the one
|
||||
//! that is most endorsed in the Polkadot ecosystem.
|
||||
//!
|
||||
//! > Note that the interoperability promised by Polkadot is unparalleled in that any two parachains
|
||||
//! > connected to Polkadot have the same security and can have much better guarantees about the
|
||||
//! > security of the recipient of any message. TODO: weakest link in bridges systems. https://github.com/paritytech/polkadot-sdk-docs/issues/66
|
||||
//!
|
||||
//! Polkadot delivers the above vision, alongside a flexible means for parachains to schedule
|
||||
//! themselves with the Relay Chain. To achieve this, Polkadot has been developed with an
|
||||
//! architecture similar to that of a computer. Polkadot Relay Chain has a number of "cores". Each
|
||||
//! core is (in simple terms) capable of progressing 1 parachain at a time. For example, a parachain
|
||||
//! can schedule itself on a single core for 5 relay chain blocks.
|
||||
//!
|
||||
//! Within the scope of Polkadot 1.x, two main scheduling ways have been considered:
|
||||
//!
|
||||
//! * Long term Parachains, obtained through locking a sum of DOT in an auction system.
|
||||
//! * on-demand Parachains, purchased through paying DOT to the relay-chain whenever needed.
|
||||
//!
|
||||
//! ### The Future
|
||||
//!
|
||||
//! After delivering Polkadot 1.x, the future of Polkadot 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 Polkadot 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 Polkadot 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 Polkadot Ubiquitous Computer.
|
||||
// TODO: add more context and explanations about Polkadot as the Ubiquitous Computer and related
|
||||
// tech. https://github.com/paritytech/polkadot-sdk-docs/issues/66
|
||||
@@ -0,0 +1,9 @@
|
||||
//! # Smart Contracts
|
||||
//!
|
||||
//! TODO: @cmichi https://github.com/paritytech/polkadot-sdk-docs/issues/56
|
||||
//!
|
||||
//! - 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,151 @@
|
||||
//! # 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 Polkadot 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 achieve 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. 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::polkadot_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::polkadot_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 [`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}.wasm`.
|
||||
//!
|
||||
//! ### Binaries
|
||||
//!
|
||||
//! Multiple binaries are shipped with substrate, the most important of which are located in the
|
||||
//! [`./bin`](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/bin) folder.
|
||||
//!
|
||||
//! * [`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.
|
||||
//! * [`chain_spec_builder`]: Utility to build more detailed chain-specs for the aforementioned
|
||||
//! node. Other projects typically contain a `build-spec` subcommand that does the same.
|
||||
//! * [`node_template`](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/bin/node-template):
|
||||
//! a template node that contains a minimal set of features and can act as a starting point of a
|
||||
//! project.
|
||||
//! * [`subkey`]: Substrate's key management utility.
|
||||
//!
|
||||
//! ### 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/paritytech/polkadot-sdk/issues/5> for an update on how the node side
|
||||
//! > components are being amalgamated.
|
||||
//!
|
||||
//! ## Parachain?
|
||||
//!
|
||||
//! As noted above, Substrate is the main engine behind the Polkadot ecosystem. One of the ways
|
||||
//! through which Polkadot can be utilized is by building "parachains", blockchains that are
|
||||
//! connected to Polkadot's shared security.
|
||||
//!
|
||||
//! To build a parachain, one could use [Cumulus](crate::polkadot_sdk::cumulus), the library on
|
||||
//! top of Substrate, empowering any substrate-based chain to be a Polkadot parachain.
|
||||
//!
|
||||
//! ## 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/paritytech/polkadot-sdk-docs/issues/57)
|
||||
//! - [`sc_consensus_manual_seal`]
|
||||
//! - [`sc_consensus_pow`]
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use crate::polkadot_sdk;
|
||||
@@ -0,0 +1,45 @@
|
||||
//! # Templates
|
||||
//!
|
||||
//! ### Internal
|
||||
//!
|
||||
//! The following templates are maintained as a part of the `polkadot-sdk` repository:
|
||||
//!
|
||||
//! - classic [`substrate-node-template`]: is a white-labeled substrate-based blockchain with a
|
||||
//! moderate amount of features. It can act as a great starting point for those who want to learn
|
||||
//! Substrate/FRAME and want to have a template that is already doing something.
|
||||
//! - [`substrate-minimal-template`]: Same as the above, but it contains the least amount of code in
|
||||
//! both the node and runtime. It is a great starting point for those who want to deeply learn
|
||||
//! Substrate and FRAME.
|
||||
//! - classic [`cumulus-parachain-template`], which is the de-facto parachain template shipped with
|
||||
//! Cumulus. It is the parachain-enabled version of [`substrate-node-template`].
|
||||
//!
|
||||
//! ### External Templates
|
||||
//!
|
||||
//! Noteworthy templates outside of this repository.
|
||||
//!
|
||||
//! - [`extended-parachain-template`](https://github.com/paritytech/extended-parachain-template): A
|
||||
//! parachain template that contains more built-in functionality such as assets and NFTs.
|
||||
//! - [`frontier-parachain-template`](https://github.com/paritytech/frontier-parachain-template): A
|
||||
//! parachain template for launching EVM-compatible parachains.
|
||||
//!
|
||||
//! [`substrate-node-template`]: https://github.com/paritytech/polkadot-sdk/blob/master/substrate/bin/node-template/
|
||||
//! [`substrate-minimal-template`]: https://github.com/paritytech/polkadot-sdk/blob/master/substrate/bin/minimal/
|
||||
//! [`cumulus-parachain-template`]: https://github.com/paritytech/polkadot-sdk/blob/master/cumulus/parachain-template/
|
||||
|
||||
// TODO: in general, we need to make a deliberate choice here of moving a few key templates to this
|
||||
// repo (nothing stays in `substrate-developer-hub`) and the everything else should be community
|
||||
// maintained. https://github.com/paritytech/polkadot-sdk-docs/issues/67
|
||||
|
||||
// TODO: we should rename `substrate-node-template` to `substrate-basic-template`,
|
||||
// `substrate-blockchain-template`. `node` is confusing in the name.
|
||||
// `substrate-blockchain-template` and `cumulus-parachain-template` go well together 🤝. https://github.com/paritytech/polkadot-sdk-docs/issues/67
|
||||
|
||||
// NOTE: a super important detail that I am looking forward to here is
|
||||
// <https://github.com/paritytech/polkadot-sdk/issues/62#issuecomment-1691523754> and
|
||||
// <https://github.com/paritytech/polkadot-sdk/issues/5>. Meaning that I would not spend time on
|
||||
// teaching someone too much detail about the ugly thing we call "node" nowadays. In the future, I
|
||||
// am sure we will either have a better "node-builder" code that can actually be tested, or an
|
||||
// "omni-node" that can run (almost) any wasm file. We should already build tutorials in this
|
||||
// direction IMO. This also affects all the templates. If we have a good neat runtime file, which we
|
||||
// are moving toward, and a good node-builder, we don't need all of these damn templates. These
|
||||
// templates are only there because the boilerplate is super horrible atm.
|
||||
@@ -0,0 +1,5 @@
|
||||
//! # XCM
|
||||
//!
|
||||
//! @KiChjang @franciscoaguirre
|
||||
//! TODO: RFCs, xcm-spec, the future of the repo, minimal example perhaps, forward to where actual
|
||||
//! docs are hosted. https://github.com/paritytech/polkadot-sdk-docs/issues/58
|
||||
@@ -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 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,4 @@
|
||||
//! Chain spec and genesis build.
|
||||
//!
|
||||
//! What is chain-spec.
|
||||
//! What is genesis state and how to build it.
|
||||
@@ -0,0 +1,7 @@
|
||||
//! # Command Line Arguments
|
||||
//!
|
||||
//!
|
||||
//! Notes:
|
||||
//!
|
||||
//! - Command line arguments of a typical substrate based chain, and how to find and learn them.
|
||||
//! - How to extend them with your custom stuff.
|
||||
@@ -0,0 +1,6 @@
|
||||
//! Consensus Swapping
|
||||
//!
|
||||
//! Notes:
|
||||
//!
|
||||
//! - The typical workshop done by Joshy in some places where he swaps out the consensus to be PoW.
|
||||
//! - This could also be a tutorial rather than a ref doc, depending on the size.
|
||||
@@ -0,0 +1,277 @@
|
||||
//! # 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 Polkadot, 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 4 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_maybe_signature,
|
||||
//! 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,
|
||||
//! signed_extensions_extra,
|
||||
//! )
|
||||
//! ```
|
||||
//!
|
||||
//! Each of the details to be concatenated together is explained below:
|
||||
//!
|
||||
//! ### version_and_signed
|
||||
//!
|
||||
//! This is one byte, equal to `0x84` or `0b1000_0100` (i.e. an upper 1 bit to denote that it is
|
||||
//! signed, and then the transaction version, 4, in the lower bits).
|
||||
//!
|
||||
//! ### 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 Polkadot 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 Polkadot 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 Polkadot relay chain is [`sp_runtime::MultiSignature`]; the
|
||||
//! variants there are the types of signature that can be provided.
|
||||
//!
|
||||
//! ### signed_extensions_extra
|
||||
//!
|
||||
//! This is the concatenation of the [SCALE encoded][frame::deps::codec] bytes representing each of
|
||||
//! the [_signed extensions_][sp_runtime::traits::SignedExtension], and are configured by the
|
||||
//! fourth generic parameter of [`sp_runtime::generic::UncheckedExtrinsic`]. Learn more about
|
||||
//! signed extensions [here][crate::reference_docs::signed_extensions].
|
||||
//!
|
||||
//! When it comes to constructing an extrinsic, each signed extension has two things that we are
|
||||
//! interested in here:
|
||||
//!
|
||||
//! - The actual SCALE encoding of the signed extension type itself; this is what will form our
|
||||
//! `signed_extensions_extra` bytes.
|
||||
//! - An `AdditionalSigned` type. This is SCALE encoded into the `signed_extensions_additional` data
|
||||
//! of the _signed payload_ (see below).
|
||||
//!
|
||||
//! Either (or both) of these can encode to zero bytes.
|
||||
//!
|
||||
//! Each chain configures the set of signed extensions that it uses in its runtime configuration.
|
||||
//! At the time of writing, Polkadot configures them
|
||||
//! [here](https://github.com/polkadot-fellows/runtimes/blob/1dc04eb954eadf8aadb5d83990b89662dbb5a074/relay/polkadot/src/lib.rs#L1432C25-L1432C25).
|
||||
//! Some of the common signed extensions are defined
|
||||
//! [here][frame::deps::frame_system#signed-extensions].
|
||||
//!
|
||||
//! Information about exactly which signed 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_composite_enums].
|
||||
//!
|
||||
//! 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 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 = concat(
|
||||
//! call_data,
|
||||
//! signed_extensions_extra,
|
||||
//! signed_extensions_additional,
|
||||
//! )
|
||||
//!
|
||||
//! if length(signed_payload) > 256 {
|
||||
//! signed_payload = blake2_256(signed_payload)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The bytes representing `call_data` and `signed_extensions_extra` can be obtained as descibed
|
||||
//! above. `signed_extensions_additional` is constructed by SCALE encoding the
|
||||
//! ["additional signed" data][sp_runtime::traits::SignedExtension::AdditionalSigned] for each
|
||||
//! signed extension that the chain is using, in order.
|
||||
//!
|
||||
//! Once we've concatenated those together, we hash the result if it's greater than 256 bytes in
|
||||
//! length 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 signed 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 parity_scale_codec::{Decode, Encode};
|
||||
|
||||
// The outer enum composes calls within
|
||||
// different pallets together. We have two
|
||||
// pallets, "PalletA" and "PalletB".
|
||||
#[derive(Encode, Decode)]
|
||||
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)]
|
||||
pub enum PalletACall {
|
||||
#[codec(index = 0)]
|
||||
Foo(String),
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
pub enum PalletBCall {
|
||||
#[codec(index = 0)]
|
||||
Bar(String),
|
||||
}
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
pub mod encoding_example {
|
||||
use super::call_data::{Call, PalletACall};
|
||||
use crate::reference_docs::signed_extensions::signed_extensions_example;
|
||||
use parity_scale_codec::Encode;
|
||||
use sp_core::crypto::AccountId32;
|
||||
use sp_keyring::sr25519::Keyring;
|
||||
use sp_runtime::{
|
||||
generic::{SignedPayload, UncheckedExtrinsic},
|
||||
MultiAddress, MultiSignature,
|
||||
};
|
||||
|
||||
// Define some signed extensions to use. We'll use a couple of examples
|
||||
// from the signed extensions reference doc.
|
||||
type SignedExtensions =
|
||||
(signed_extensions_example::AddToPayload, signed_extensions_example::AddToSignaturePayload);
|
||||
|
||||
// We'll use `UncheckedExtrinsic` to encode our extrinsic for us. We set
|
||||
// the address and signature type to those used on Polkadot, use our custom
|
||||
// `Call` type, and use our custom set of `SignedExtensions`.
|
||||
type Extrinsic =
|
||||
UncheckedExtrinsic<MultiAddress<AccountId32, ()>, Call, MultiSignature, SignedExtensions>;
|
||||
|
||||
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 signed extensions.
|
||||
let signed_extensions = (
|
||||
signed_extensions_example::AddToPayload(1),
|
||||
signed_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,
|
||||
// signed_extensions_extra and signed_extensions_additional, and hashing
|
||||
// the result if it's > 256 bytes:
|
||||
let signed_payload = SignedPayload::new(&call_data, signed_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, signed_extensions);
|
||||
|
||||
let encoded_ext = ext.encode();
|
||||
encoded_ext
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
//! # Fee-Less Runtime
|
||||
//!
|
||||
//!
|
||||
//! 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 tutorial/template, rather than a reference doc.
|
||||
@@ -0,0 +1,23 @@
|
||||
//! # FRAME Benchmarking and Weights.
|
||||
//!
|
||||
//! Notes:
|
||||
//!
|
||||
//! On Weight as a concept.
|
||||
//!
|
||||
//! - Why we need it. Super important. People hate this. We need to argue why it is worth it.
|
||||
//! - Axis of weight: PoV + Time.
|
||||
//! - pre dispatch weight vs. metering and post dispatch correction.
|
||||
//! - mention that we will do this for PoV
|
||||
//! - you can manually refund using `DispatchResultWithPostInfo`.
|
||||
//! - Technically you can have weights with any benchmarking framework. You just need one number to
|
||||
//! be computed pre-dispatch. But FRAME gives you a framework for this.
|
||||
//! - improve documentation of `#[weight = ..]` and `#[pallet::weight(..)]`. All syntax variation
|
||||
//! should be covered.
|
||||
//!
|
||||
//! on FRAME benchmarking machinery:
|
||||
//!
|
||||
//! - component analysis, why everything must be linear.
|
||||
//! - how to write benchmarks, how you must think of worst case.
|
||||
//! - how to run benchmarks.
|
||||
//!
|
||||
//! - https://www.shawntabrizi.com/substrate/substrate-storage-deep-dive/
|
||||
@@ -0,0 +1 @@
|
||||
//! # FRAME Composite Enums
|
||||
@@ -0,0 +1,8 @@
|
||||
//! FRAME Currency Abstractions and Traits
|
||||
//!
|
||||
//! Notes:
|
||||
//!
|
||||
//! - History, `Currency` trait.
|
||||
//! - `Hold` and `Freeze` with diagram.
|
||||
//! - `HoldReason` and `FreezeReason`
|
||||
//! - This footgun: https://github.com/paritytech/polkadot-sdk/pull/1900#discussion_r1363783609
|
||||
@@ -0,0 +1,14 @@
|
||||
//! # FRAME Origin
|
||||
//!
|
||||
//! Notes:
|
||||
//!
|
||||
//! - Def talk about account abstraction and how it is a solved issue in frame. See Gav's talk in
|
||||
//! Protocol Berg 2023
|
||||
//! - system's raw origin, how it is amalgamated with other origins into one type
|
||||
//! [`frame_composite_enums`]
|
||||
//! - signed origin
|
||||
//! - unsigned origin, link to [`fee_less_runtime`]
|
||||
//! - Root origin, how no one can obtain it.
|
||||
//! - Abstract origin: how FRAME allows you to express "origin is 2/3 of the this body or 1/2 of
|
||||
//! that body or half of the token holders".
|
||||
//! - `type CustomOrigin: EnsureOrigin<_>` in pallets.
|
||||
@@ -0,0 +1,9 @@
|
||||
//! # Runtime Runtime Upgrade and Testing
|
||||
//!
|
||||
//!
|
||||
//! Notes:
|
||||
//!
|
||||
//! - Flow of things, when does `on_runtime_upgrade` get called. Link to to `Hooks` and its diagram
|
||||
//! as source of truth.
|
||||
//! - Data migration and when it is needed.
|
||||
//! - Look into the pba-lecture.
|
||||
@@ -0,0 +1,8 @@
|
||||
//! # FRAME Accounts
|
||||
//!
|
||||
//! 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://substrate.stackexchange.com/questions/263/what-is-the-meaning-of-the-account-provider-sufficients-and-consumer
|
||||
@@ -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.
|
||||
//!
|
||||
//! #### Parachain
|
||||
//!
|
||||
//! Short for "parallelized chain" a parachain is a specialized blockchain that runs in parallel to
|
||||
//! the Relay Chain (Polkadot, Kusama, etc.), benefiting from the shared security and
|
||||
//! interoperability features of it.
|
||||
//!
|
||||
//! **Synonyms**: AppChain
|
||||
//!
|
||||
//! #### PVF
|
||||
//! The Parachain Validation Function (PVF) is the current runtime Wasm for a parachain that is
|
||||
//! stored on the Relay chain. It is an essential component in the Polkadot ecosystem, encapsulating
|
||||
//! the validation logic for each parachain. The PVF is executed by validators to verify the
|
||||
//! correctness of parachain blocks. This is critical for ensuring that each block follows the logic
|
||||
//! set by its respective parachain, thus maintaining the integrity and security of the entire
|
||||
//! network.
|
||||
//!
|
||||
//! **Synonyms**: Parachain Validation Function
|
||||
@@ -0,0 +1,7 @@
|
||||
//! # Light Clients
|
||||
//!
|
||||
//!
|
||||
//! Notes: should contain only high level information about light clients, then link to how to set
|
||||
//! it up in PAPI and SubXT
|
||||
//! https://docs.substrate.io/learn/light-clients-in-substrate-connect/
|
||||
//! https://github.com/substrate-developer-hub/substrate-front-end-template/pull/277
|
||||
@@ -0,0 +1 @@
|
||||
//! # Metadata
|
||||
@@ -0,0 +1,99 @@
|
||||
//! # Polkadot SDK Reference Docs.
|
||||
//!
|
||||
//! This is the entry point for all reference documents that enhance one's learning experience in
|
||||
//! the Polkadot 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#why-rust-docs) and our documentation
|
||||
//! [principles](crate#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;
|
||||
|
||||
/// Learn about the signed extensions that form a part of extrinsics.
|
||||
// TODO: @jsdw https://github.com/paritytech/polkadot-sdk-docs/issues/42
|
||||
pub mod signed_extensions;
|
||||
|
||||
/// Learn about *"Origin"* A topic in FRAME that enables complex account abstractions to be built.
|
||||
// TODO: @shawntabrizi https://github.com/paritytech/polkadot-sdk-docs/issues/43
|
||||
pub mod frame_origin;
|
||||
|
||||
/// Learn about how to write safe and defensive code in your FRAME runtime.
|
||||
// TODO: @CrackTheCode016 https://github.com/paritytech/polkadot-sdk-docs/issues/44
|
||||
pub mod safe_defensive_programming;
|
||||
|
||||
/// Learn about composite enums in FRAME-based runtimes, such as "RuntimeEvent" and "RuntimeCall".
|
||||
pub mod frame_composite_enums;
|
||||
|
||||
/// 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.
|
||||
// TODO: @jsdw https://github.com/paritytech/polkadot-sdk-docs/issues/47
|
||||
pub mod metadata;
|
||||
|
||||
/// Learn about how frame-system handles `account-ids`, nonces, consumers and providers.
|
||||
pub mod frame_system_accounts;
|
||||
|
||||
/// Learn about the currency-related abstractions provided in FRAME.
|
||||
pub mod frame_currency;
|
||||
|
||||
/// Learn about benchmarking and weight.
|
||||
// TODO: @shawntabrizi @ggwpez https://github.com/paritytech/polkadot-sdk-docs/issues/50
|
||||
pub mod frame_benchmarking_weight;
|
||||
|
||||
/// Learn about chain specification file and the genesis state of the blockchain.
|
||||
// TODO: @michalkucharczyk https://github.com/paritytech/polkadot-sdk-docs/issues/51
|
||||
pub mod chain_spec_genesis;
|
||||
|
||||
/// Learn about all the memory limitations of the WASM runtime when it comes to memory usage.
|
||||
// TODO: @kianenigma https://github.com/paritytech/polkadot-sdk-docs/issues/52
|
||||
pub mod wasm_memory;
|
||||
|
||||
/// Learn about Substrate's CLI, and how it can be extended.
|
||||
// TODO: @kianenigma https://github.com/paritytech/polkadot-sdk-docs/issues/53
|
||||
pub mod cli;
|
||||
|
||||
/// Learn about Substrate's consensus algorithms, and how you can switch between two.
|
||||
// TODO: @JoshOrndorff @kianenigma https://github.com/paritytech/polkadot-sdk-docs/issues/54
|
||||
pub mod consensus_swapping;
|
||||
|
||||
/// Learn about all the advance ways to test your coordinate a rutnime upgrade and data migration.
|
||||
// TODO: @liamaharon https://github.com/paritytech/polkadot-sdk-docs/issues/55
|
||||
pub mod frame_runtime_migration;
|
||||
|
||||
/// Learn about light nodes, how they function, and how Substrate-based chains come
|
||||
/// light-node-first out of the box.
|
||||
// TODO: @jsdw @josepot https://github.com/paritytech/polkadot-sdk-docs/issues/68
|
||||
pub mod light_nodes;
|
||||
@@ -0,0 +1,6 @@
|
||||
//! Runtime vs. Smart Contracts
|
||||
//!
|
||||
//! Notes:
|
||||
//!
|
||||
//! Why one can be weighed, and one MUST be metered.
|
||||
//! https://forum.polkadot.network/t/where-contracts-fail-and-runtimes-chains-are-needed/4464/3
|
||||
@@ -0,0 +1 @@
|
||||
//!
|
||||
@@ -0,0 +1,79 @@
|
||||
//! Signed extensions are, briefly, a means for different chains to extend the "basic" extrinsic
|
||||
//! format with custom data that can be checked by the runtime.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! Defining a couple of very simple signed extensions looks like the following:
|
||||
#![doc = docify::embed!("./src/reference_docs/signed_extensions.rs", signed_extensions_example)]
|
||||
|
||||
#[docify::export]
|
||||
pub mod signed_extensions_example {
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::traits::SignedExtension;
|
||||
|
||||
// This doesn't actually check anything, but simply allows
|
||||
// some arbitrary `u32` to be added to the extrinsic payload
|
||||
#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
|
||||
pub struct AddToPayload(pub u32);
|
||||
|
||||
impl SignedExtension for AddToPayload {
|
||||
const IDENTIFIER: &'static str = "AddToPayload";
|
||||
type AccountId = ();
|
||||
type Call = ();
|
||||
type AdditionalSigned = ();
|
||||
type Pre = ();
|
||||
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<
|
||||
Self::AdditionalSigned,
|
||||
sp_runtime::transaction_validity::TransactionValidityError,
|
||||
> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// This is the opposite; nothing will be added to the extrinsic payload,
|
||||
// but the AdditionalSigned type (`1234u32`) will be added to the
|
||||
// payload to be signed.
|
||||
#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
|
||||
pub struct AddToSignaturePayload;
|
||||
|
||||
impl SignedExtension for AddToSignaturePayload {
|
||||
const IDENTIFIER: &'static str = "AddToSignaturePayload";
|
||||
type AccountId = ();
|
||||
type Call = ();
|
||||
type AdditionalSigned = u32;
|
||||
type Pre = ();
|
||||
|
||||
fn additional_signed(
|
||||
&self,
|
||||
) -> Result<
|
||||
Self::AdditionalSigned,
|
||||
sp_runtime::transaction_validity::TransactionValidityError,
|
||||
> {
|
||||
Ok(1234)
|
||||
}
|
||||
|
||||
fn pre_dispatch(
|
||||
self,
|
||||
_who: &Self::AccountId,
|
||||
_call: &Self::Call,
|
||||
_info: &sp_runtime::traits::DispatchInfoOf<Self::Call>,
|
||||
_len: usize,
|
||||
) -> Result<Self::Pre, sp_runtime::transaction_validity::TransactionValidityError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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://substrate.stackexchange.com/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::*;
|
||||
|
||||
// Simple 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,7 @@
|
||||
//! # WASM Memory Limitations.
|
||||
//!
|
||||
//! Notes:
|
||||
//!
|
||||
//! - Stack: Need to use `Box<_>`
|
||||
//! - Heap: Substrate imposes a limit. PvF execution has its own limits
|
||||
//! - Heap: There is also a maximum amount that a single allocation can have.
|
||||
@@ -0,0 +1,113 @@
|
||||
//! # WASM Meta Protocol
|
||||
//!
|
||||
//! All Substrate based chains adhere to a unique architectural design novel to the Polkadot
|
||||
//! 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 the blockchain ecosystem.
|
||||
//!
|
||||
//! 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`](../../../sp_core/index.html)) 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**, 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 updates 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 TODO 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 also 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 Polkadot always requires an RFC.
|
||||
//!
|
||||
//! ## 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 a 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 now 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
|
||||
//!
|
||||
//! TODO
|
||||
//!
|
||||
//!
|
||||
//! ## Example: Block Execution.
|
||||
//!
|
||||
//! TODO
|
||||
Reference in New Issue
Block a user