Skip to content

Commit

Permalink
make intro a simple starting paragraph
Browse files Browse the repository at this point in the history
  • Loading branch information
shawntabrizi committed Aug 6, 2024
1 parent 36d10ac commit b6c92fa
Show file tree
Hide file tree
Showing 193 changed files with 2,506 additions and 2,490 deletions.
65 changes: 4 additions & 61 deletions steps/0/README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,9 @@
# Introduction

Welcome to the new Substrate Collectables Workshop.
Welcome to the Substrate Collectables Workshop.

The goal of this tutorial is to **teach by experience** various entry level concepts around developing on [Polkadot](https://polkadot.network/).
The goal of this tutorial is to **teach by experience** various entry level concepts around developing with the [Polkadot SDK](https://github.com/paritytech/polkadot-sdk).

Before we get started writing code, we will need to cover some basics.
This tutorial will walk you through the steps of building an NFT Marketplace, themed around collectable digital pets called Substrate Kitties!

## Prerequisites

The tutorial is designed to be completed by anyone with basic familiarity with [Rust](https://www.rust-lang.org/), and little to no familiarity with the [Polkadot SDK](https://github.com/paritytech/polkadot-sdk).

If you do not feel comfortable with the level of Rust used in this tutorial, we recommend you first check out the [`rust-state-machine`](https://github.com/shawntabrizi/rust-state-machine) tutorial.

## Tutorial Structure

This tutorial is broken up into small steps with documentation and code.

Documentation should teach you all the concepts needed to complete the tutorial, while the code will ensure that you can actually execute on the knowledge gained.

The code in each step should have comments marked `TODO`, which indicate the actions you need to take in order to complete the step successfully.

At each step, we include a `diff` view so that you can see what has changed from the last step (new action items), and what should change to complete the step (the solution).

At the end of each step, you should be able to run all the following commands without any errors or warnings:

- `cargo +nightly fmt`
- `cargo +nightly clippy`
- `cargo test`

We recommend you run these during each step in the tutorial to confirm you are doing everything correctly.

## Clone the Starting Template

The best way to follow this tutorial is to clone the starting template, and follow the steps of the tutorial locally.

Run the following:

```bash
git clone https://github.com/shawntabrizi/substrate-collectables-workshop/ -b starting-template --single-branch
cd substrate-collectables-workshop
```

Or access the template directly here:

[https://github.com/shawntabrizi/substrate-collectables-workshop/releases/tag/starting-template](https://github.com/shawntabrizi/substrate-collectables-workshop/releases/tag/starting-template)

The starting template includes a `README` with instructions to setup your working environment. Follow those instructions.

Make sure you are able to run the following checks on this starting template without warnings or errors:

```bash
cargo +nightly fmt
cargo +nightly clippy
cargo test
```

It may take a while for this to complete based on how powerful your computer is.

Feel free to move onto the next steps while these checks are compiling.

## Contribute

This tutorial is completely free to use, modify, and distribute in any way you see fit.

If you catch any problems with the tutorial or have ideas on how it can be improved, open an [issue](https://github.com/shawntabrizi/substrate-collectables-workshop/issues) or a [pull request](https://github.com/shawntabrizi/substrate-collectables-workshop/pulls).
Let's get started!
123 changes: 37 additions & 86 deletions steps/1/README.md
Original file line number Diff line number Diff line change
@@ -1,113 +1,64 @@
# Polkadot-SDK
# Setup

Our starting template for this tutorial uses the [Polkadot SDK](https://github.com/paritytech/polkadot-sdk).
Before we start writing code, we will need to setup your computer for this tutorial.

This is the same technology stack used to build and power the [Polkadot Network](https://polkadot.network/).
## Prerequisites

To better understand what you will be doing in this tutorial, we need to start with a high level overview of blockchains.
The tutorial is designed to be completed by anyone with basic familiarity with [Rust](https://www.rust-lang.org/), and little to no familiarity with the [Polkadot SDK](https://github.com/paritytech/polkadot-sdk).

## Blockchain
If you do not feel comfortable with the level of Rust used in this tutorial, we recommend you first check out the [`rust-state-machine`](https://github.com/shawntabrizi/rust-state-machine) tutorial.

Blockchains are the foundation of building Web3 technologies.
## Tutorial Structure

Web3 is a promise toward a world with less trust, and more truth.
This tutorial is broken up into small steps with documentation and code.

Through blockchain technology, we are able to develop and deploy software that are decentralized, open, permissionless, censorship resistant, and independently verifiable.
Documentation should teach you all the concepts needed to complete the tutorial, while the code will ensure that you can actually execute on the knowledge gained.

The main purpose of a blockchain node is to come to consensus with other nodes on the decentralized network.
The code in each step should have comments marked `TODO`, which indicate the actions you need to take in order to complete the step successfully.

<details>
At each step, we include a `diff` view so that you can see what has changed from the last step (new action items), and what should change to complete the step (the solution).

<summary>Deep Dive</summary>
At the end of each step, you should be able to run all the following commands without any errors or warnings:

If you want to learn more about blockchains, check out the following video from the Polkadot Blockchain Academy:
- `cargo +nightly fmt`
- `cargo +nightly clippy`
- `cargo test`

<iframe width="560" height="315" src="https://www.youtube.com/embed/8UvdfFGYFiE?si=5PIyppVBZ91vUtjf" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
We recommend you run these during each step in the tutorial to confirm you are doing everything correctly.

</details>
## Clone the Starting Template

## Runtime
The best way to follow this tutorial is to clone the starting template, and follow the steps of the tutorial locally.

At the heart of a blockchain is a [state transition function](https://en.wikipedia.org/wiki/Finite-state_machine) (STF).
Run the following:

This is the logic of the blockchain, and defines all the ways a blockchain is allowed to manipulate the blockchain state.
```bash
git clone https://github.com/shawntabrizi/substrate-collectables-workshop/ -b starting-template --single-branch
cd substrate-collectables-workshop
```

In the `polkadot-sdk` we refer to this logic as the blockchain's runtime.
Or access the template directly here:

All nodes on a blockchain network have and use the same runtime, allowing them to come to consensus about changes to a blockchain.
[https://github.com/shawntabrizi/substrate-collectables-workshop/releases/tag/starting-template](https://github.com/shawntabrizi/substrate-collectables-workshop/releases/tag/starting-template)

<details>
### Install Dependencies

<summary>Deep Dive</summary>
The starting template includes a `README` with instructions to setup your working environment. Follow those instructions.

To learn more about the runtime, and its role inside of the `polkadot-sdk`, check out this video from the Polkadot Blockchain Academy:
Make sure you are able to run the following checks on this starting template without warnings or errors:

<iframe width="560" height="315" src="https://www.youtube.com/embed/-ttmm8gYS04?si=ZH_g83CVtguENoK7" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
```bash
cargo +nightly fmt
cargo +nightly clippy
cargo test
```

</details>
It may take a while for this to complete based on how powerful your computer is.

## FRAME
Feel free to move onto the next steps while these checks are compiling.

The `polkadot-sdk` provides a developer framework called FRAME.
## Contribute

FRAME is an opinionated framework on how one should quickly and easily build and maintain a blockchain's runtime.
This tutorial is completely free to use, modify, and distribute in any way you see fit.

> NOTE: It is important to clarify that FRAME is not the only way you can develop a runtime for the `polkadot-sdk`, but it is the one that the Polkadot Network uses and is most supported by the ecosystem.
You can see in our project, nearly all of our dependencies come from a single crate named [`frame`](https://docs.rs/polkadot-sdk-frame/0.6.0/polkadot_sdk_frame/index.html).

This crate is really just a convenience wrapper around other smaller crates, all exposed through [`frame::deps`](https://docs.rs/polkadot-sdk-frame/0.6.0/polkadot_sdk_frame/deps/index.html).

For our tutorial, most of the types and traits we need access to are automatically brought into scope through [`frame::prelude::*`](https://docs.rs/polkadot-sdk-frame/0.6.0/polkadot_sdk_frame/prelude/index.html), however once in a while, we will need to import something more specific from [`frame::primitives`](https://docs.rs/polkadot-sdk-frame/0.6.0/polkadot_sdk_frame/primitives/index.html) or [`frame::traits`](https://docs.rs/polkadot-sdk-frame/0.6.0/polkadot_sdk_frame/traits/index.html).

## Pallets

FRAME's key decision is to break apart the blockchain runtime into separate logical pieces that can choose to interact with one another.

These logical pieces are called Pallets.

TODO: Add images.

You can think of different Pallets as different applications or functions that your blockchain exposes.

You can also think of Pallets very similar to traditional blockchain smart contracts, however Pallets are more powerful and execute much faster than smart contracts.

<details>

<summary>Deep Dive</summary>

To learn more about FRAME and Pallets, check out this video from the Polkadot Blockchain Academy:

<iframe width="560" height="315" src="https://www.youtube.com/embed/ghMloMzEEsA?si=3DtsmrYOapbnR2oy" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

</details>

## NFTs

Non-Fungible Tokens (NFTs) are a type of token which can be created and traded on a blockchain.

As their name indicated, each NFT is totally unique, and therefore non-fungible with one another.

NFTs can be used for many things, for example:

- Representing real world assets
- Ownership Rights
- Access Rights
- Digital assets
- Music
- Images
- Skins
- Characters
- and much more...

## Starting Template

The template for this project is a minimal starting point for developing a custom Pallet.

In this tutorial, we will create a Pallet which allows us to create and manage a collection of NFTs.

Our NFTs will represent kitties, which will be a digital pet that can be created, traded, and more.

This Pallet could then be included into a `polkadot-sdk` project and used to launch a Web3 application on the Polkadot Network.

TODO: need to create and link to a tutorial creating and launching a runtime with omninode.
If you catch any problems with the tutorial or have ideas on how it can be improved, open an [issue](https://github.com/shawntabrizi/substrate-collectables-workshop/issues) or a [pull request](https://github.com/shawntabrizi/substrate-collectables-workshop/pulls).
4 changes: 2 additions & 2 deletions steps/1/gitorial_metadata.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"_Note": "This file will not be included in your final gitorial.",
"commitMessage": "action: learn about the polkadot sdk"
}
"commitMessage": "action: setup"
}
9 changes: 0 additions & 9 deletions steps/1/src/impls.rs

This file was deleted.

37 changes: 0 additions & 37 deletions steps/1/src/lib.rs

This file was deleted.

65 changes: 49 additions & 16 deletions steps/10/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,61 @@
# Storage Values
# Storage Basics

The most basic storage type for a blockchain is a single `StorageValue`.
Now that we have covered the basics of Pallets, and gone through all of the template code, we can start writing some code ourselves.

A `StorageValue` is used to place a single object into the blockchain storage.
In this section you will learn the basics of creating and using storage in your Pallet.

A single object can be as simple as a single type like a `u32`, or more complex structures, or even vectors.
But before we can start coding, we need to learn some basics about blockchains.

What is most important to understand is that a `StorageValue` places a single entry into the merkle trie. So when you read data, you read all of it. When you write data, you write all of it. This is in contrast to a `StorageMap`, which you will learn about next.
## Hash Functions

## Construction
Hash functions are an important tool throughout blockchain development.

We constructed a simple `StorageValue` for you in the code, but let's break it down:
A hash function takes an arbitrary sized input and returns a fixed-size string of bytes.

```rust
#[pallet::storage]
pub(super) type CountForKitties<T: Config> = StorageValue<Value = u32>;
```
This output, usually called a hash, is unique to each unique input. Even a small change to the input creates a dramatic change to the output.

As you can see, our storage is a type alias for a new instance of `StorageValue`.
Hash functions have several key properties:

Our storage value has a parameter `Value` where we can define the type we want to place in storage. In this case, it is a simple `u32`.
- Deterministic: The same input always produces the same output.
- Pre-image Resistant: It is difficult to derive the original input from its hash value.
- Collision Resistant: It’s hard to find two different inputs that produce the same hash output.

You will also notice `CountForKitties` is generic over `<T: Config>`. All of our storage must be generic over `<T: Config>` even if we are not using it directly. Macros use this generic parameter to fill in behind the scene details to make the `StorageValue` work. Think about all the code behind the scenes which actually sticks this storage into a merkle trie in the database of our blockchain.
These properties make hash functions key for ensuring data integrity and uniqueness in blockchain technology.

Visibility of the type is up to you and your needs, but you need to remember that blockchains are public databases. So `pub` in this case is only about Rust, and allowing other modules to access this storage and its APIs directly.
## Hash Fingerprint

You cannot make storage on a blockchain "private", and even if you make this storage without `pub`, there are low level ways to manipulate the storage in the database.
Due to the properties of a Hash, it is often referred to as a fingerprint.

For context, a 32-byte hash has 2^32 different possible outputs. This nearly as many atoms as there are in the whole universe!

This uniqueness property helps blockchain nodes come to consensus with one another.

Rather than needing to compare all the data in their blockchain database with one another, they can simply share the hash of that database, and know in a single small comparison if all data in that database is the same.

Remember, if there were any small differences between their databases, even just one bit in a multi-terabyte database being different, the resulting hash would dramatically change, and they would know their databases are not the same.

## Merkle Trie

A merkle trie is a data structure which is constructed using a hash function.

Rather than hashing the whole database into a single hash, we create a tree of hashes.

For example, we take pairs of data, combine them, then hash them to generate a new output. Then we take pairs of hashes, combine them, then hash them to generate another new output.

We can repeat this process until we are left with a single hash called the "root hash". This process literally creates a tree of hashes.

Just like before, we can use a single hash to represent the integrity of all data underneath it, but now we can efficiently represent specific pieces of data in the database using the path down the trie to that data.

It is called a merkle "trie" because the trie data structure is used to reduce the amount of redundant data stored in the tree.

### Complexity

The reason we go into this much detail about merkle tries is that they increase the complexity in reading and writing to the blockchain database.

Whereas reading and writing to a database could be considered `O(1)`, a merklized database has read and write complexity of `O(log N)`, where `N` is the total number of items stored in the database.

This additional complexity means that designing storage for a blockchain is an extremely important and sensitive operation.

The primary advantage of using a merkle trie is that proving specific data exists inside the database is much more efficient! Whereas you would normally need to share the whole database to prove that some data exists, with a merklized database, you only need to share `O(log N)` amount of data.

In this next section, and throughout the tutorial, we will start to explore some of those decisions.
4 changes: 2 additions & 2 deletions steps/10/gitorial_metadata.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"_Note": "This file will not be included in your final gitorial.",
"commitMessage": "template: learn about storage value"
}
"commitMessage": "section: storage basics"
}
6 changes: 0 additions & 6 deletions steps/10/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ pub mod pallet {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}

/* 🚧 TODO 🚧:
- Create a new `StorageValue` named `CountForKitties`.
- `CountForKitties` should be generic over `<T: Config>`.
- Set `Value` to `u32` to store that type.
*/

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Expand Down
Loading

0 comments on commit b6c92fa

Please sign in to comment.