From 8c03fe3656999de8f7c7c467437437051707d850 Mon Sep 17 00:00:00 2001 From: w3fdeploy Date: Mon, 28 Aug 2023 14:24:50 +0000 Subject: [PATCH] Deploy website - based on cfd7dd50e8eda08840ef7d4c8a2bfb206cf12705 --- 404.html | 4 +-- assets/js/0939dfa6.2a677a1e.js | 1 + assets/js/0939dfa6.ddb62803.js | 1 - assets/js/093f602f.4093fe70.js | 1 - assets/js/093f602f.95aa58b6.js | 1 + ...99fa2.0f3e17a7.js => 0aa99fa2.624d19e4.js} | 2 +- assets/js/14263310.68ffe623.js | 1 - assets/js/14263310.b1464a13.js | 1 + assets/js/47521d56.6e991101.js | 1 - assets/js/47521d56.ce090131.js | 1 + assets/js/4a418313.d6dba999.js | 1 - assets/js/4a418313.dfac47c1.js | 1 + assets/js/4b1e850b.57a51f36.js | 1 + assets/js/4b1e850b.9c39ccf0.js | 1 - assets/js/55a851e8.50ec5685.js | 1 - assets/js/55a851e8.5fe8eea0.js | 1 + assets/js/66f9d3f7.5377e133.js | 1 - assets/js/66f9d3f7.5c664fe1.js | 1 + assets/js/698a20df.108fd7b0.js | 1 - assets/js/698a20df.cb6be3c1.js | 1 + ...bb328.d9ddd61b.js => 845bb328.57d0cd9a.js} | 2 +- assets/js/85386164.04e37665.js | 1 - assets/js/85386164.3446a23f.js | 1 + assets/js/8568807f.0034759d.js | 1 - assets/js/8568807f.bf6000b1.js | 1 + assets/js/8a7c7b85.2ea69f52.js | 1 + assets/js/8a7c7b85.77aabbde.js | 1 - ...a237c.7a60832e.js => 9eca237c.eece0504.js} | 2 +- assets/js/ddd8c84d.6b6021cc.js | 1 + assets/js/ddd8c84d.73e2251a.js | 1 - assets/js/e159a34b.3f058361.js | 1 + assets/js/e159a34b.bc159c19.js | 1 - assets/js/fe76b6c3.572fe957.js | 1 + assets/js/fe76b6c3.5f90ddbd.js | 1 - ...n.c47f1043.js => runtime~main.dc484ca7.js} | 2 +- blog.html | 4 +-- blog/Polkadot MOOC.html | 4 +-- blog/archive.html | 4 +-- blog/tags.html | 4 +-- blog/tags/mooc.html | 4 +-- blog/tags/polkadot.html | 4 +-- docs/AtoZ/account.html | 4 +-- docs/AtoZ/bridge.html | 4 +-- docs/AtoZ/collator.html | 4 +-- docs/AtoZ/democracy.html | 4 +-- docs/AtoZ/existential-deposit.html | 4 +-- docs/AtoZ/forkless.html | 4 +-- docs/AtoZ/grandpa.html | 4 +-- docs/AtoZ/hash.html | 4 +-- docs/AtoZ/interoperability.html | 4 +-- docs/AtoZ/kusama.html | 4 +-- docs/AtoZ/launch.html | 4 +-- docs/AtoZ/multisig.html | 4 +-- docs/AtoZ/npos.html | 4 +-- docs/AtoZ/on-chain-governance.html | 4 +-- "docs/AtoZ/phragm\303\251n.html" | 4 +-- docs/AtoZ/polkadot-js.html | 4 +-- docs/AtoZ/q-faq.html | 6 ++-- docs/Blockchain/Module1/bitcoin.html | 4 +-- docs/Blockchain/Module1/blockchain.html | 4 +-- docs/Blockchain/Module1/digital-money.html | 4 +-- docs/Blockchain/Module1/ethereum.html | 4 +-- docs/Blockchain/Module1/money-properties.html | 4 +-- docs/Blockchain/Module1/money-trust.html | 4 +-- docs/Blockchain/Module1/web3.html | 4 +-- docs/Blockchain/Module2/cryptography.html | 4 +-- docs/Blockchain/Module2/datastructures.html | 4 +-- docs/Blockchain/Module2/decentralization.html | 4 +-- docs/Blockchain/Module2/hash.html | 4 +-- docs/Blockchain/Module2/invincible.html | 4 +-- docs/Blockchain/Module2/keypair.html | 4 +-- docs/Blockchain/Module2/trust.html | 4 +-- docs/Blockchain/Module3/PoS.html | 4 +-- docs/Blockchain/Module3/PoW.html | 4 +-- docs/Blockchain/Module3/finality.html | 4 +-- docs/Blockchain/Module3/forking.html | 4 +-- docs/Blockchain/Module3/hashpower.html | 4 +-- docs/Blockchain/Module3/mining-hardware.html | 4 +-- docs/Blockchain/Module3/mining.html | 4 +-- docs/Blockchain/Module4/consensus.html | 4 +-- docs/Blockchain/Module4/distributed.html | 4 +-- docs/Blockchain/Module4/gossip.html | 4 +-- docs/Blockchain/Module4/lightclients.html | 4 +-- .../Module4/network-challenges.html | 4 +-- docs/Blockchain/Module4/networkstack.html | 4 +-- docs/Blockchain/Module4/nodes.html | 4 +-- docs/Blockchain/Module5/enterprise.html | 4 +-- docs/Blockchain/Module5/layer0.html | 4 +-- docs/Blockchain/Module5/layer1.html | 4 +-- docs/Blockchain/Module5/layer2.html | 4 +-- docs/Blockchain/Module5/layers.html | 4 +-- docs/Blockchain/Module5/zk-proofs.html | 4 +-- docs/Blockchain/Module6/computing.html | 4 +-- docs/Blockchain/Module6/crypto-defi.html | 4 +-- docs/Blockchain/Module6/dao.html | 4 +-- docs/Blockchain/Module6/future-web3.html | 4 +-- docs/Blockchain/Module6/nft-meta.html | 4 +-- docs/Blockchain/Module6/web3.html | 4 +-- docs/Parachain/beginner/section1.html | 4 +-- .../beginner/section1/network-components.html | 4 +-- .../beginner/section1/parachain.html | 4 +-- .../beginner/section1/relay-chain.html | 4 +-- docs/Parachain/beginner/section2.html | 4 +-- .../beginner/section2/install-binary.html | 4 +-- .../beginner/section2/install-template.html | 4 +-- .../beginner/section2/running-chains.html | 4 +-- docs/Parachain/beginner/section3.html | 4 +-- .../beginner/section3/creating-auction.html | 4 +-- .../section3/creating-parathread.html | 4 +-- .../section3/developing-parachain.html | 4 +-- docs/Parachain/beginner/section4.html | 4 +-- .../beginner/section4/initatives.html | 4 +-- .../beginner/section4/road-to-production.html | 4 +-- docs/Polkadot/Module1/architecture.html | 4 +-- docs/Polkadot/Module1/becomeparachain.html | 4 +-- docs/Polkadot/Module1/features.html | 4 +-- docs/Polkadot/Module1/polkadot.html | 4 +-- docs/Polkadot/Module1/polkadotvision.html | 4 +-- docs/Polkadot/Module2/account.html | 4 +-- docs/Polkadot/Module2/dotutility.html | 4 +-- docs/Polkadot/Module2/explorenetwork.html | 18 +++++------ docs/Polkadot/Module2/governance.html | 8 ++--- docs/Polkadot/Module2/treasury.html | 8 ++--- docs/Polkadot/Module3/consensus.html | 30 +++++++++---------- docs/Polkadot/Module3/maintainers.html | 16 +++++----- docs/Polkadot/Module3/npos.html | 13 ++++---- .../Module3/securityimprovements.html | 4 +-- docs/Polkadot/Module3/sharedsecurity.html | 4 +-- docs/Polkadot/Module4/cryptography.html | 24 +++++++-------- docs/Polkadot/Module4/decentralization.html | 8 ++--- docs/Polkadot/Module4/networking.html | 26 ++++++++-------- docs/Polkadot/Module4/nodes.html | 4 +-- docs/Polkadot/Module4/parachainblock.html | 16 +++++----- .../Module5/architectureimprovements.html | 4 +-- docs/Polkadot/Module5/bridges.html | 4 +-- docs/Polkadot/Module5/interoperability.html | 14 ++++----- docs/Polkadot/Module5/parachains.html | 4 +-- docs/Polkadot/Module5/scalability.html | 20 ++++++------- docs/Polkadot/Module6/developers.html | 4 +-- docs/Polkadot/Module6/polkadotjs.html | 4 +-- docs/Polkadot/Module6/rust.html | 4 +-- docs/Polkadot/Module6/substrate.html | 4 +-- docs/Polkadot/Module6/testnets.html | 4 +-- docs/Rust/rust-appendix.html | 4 +-- docs/Rust/section1.html | 4 +-- docs/Rust/section1/wasm-tldr.html | 4 +-- docs/Rust/section1/what-is-rust.html | 4 +-- docs/Rust/section1/why-rust.html | 4 +-- docs/Rust/section2.html | 4 +-- docs/Rust/section2/data-types.html | 4 +-- docs/Rust/section2/functions-comments.html | 4 +-- docs/Rust/section2/heap-vs-stack.html | 4 +-- docs/Rust/section2/loops.html | 4 +-- docs/Rust/section2/module2.html | 4 +-- docs/Rust/section2/variables-mutability.html | 4 +-- docs/Rust/section3.html | 4 +-- docs/Rust/section3/borrowing.html | 4 +-- docs/Rust/section3/module3.html | 8 ++--- docs/Rust/section3/ownership.html | 4 +-- docs/Rust/section3/slices.html | 4 +-- docs/Rust/section4.html | 4 +-- docs/Rust/section4/enums.html | 4 +-- docs/Rust/section4/error-handling.html | 6 ++-- docs/Rust/section4/panic.html | 4 +-- docs/Rust/section5.html | 4 +-- docs/Rust/section5/collections.html | 4 +-- docs/Rust/section5/struct-methods.html | 4 +-- docs/Rust/section5/structs.html | 4 +-- docs/Rust/section5/vectors-vs-strings.html | 4 +-- docs/Rust/section6.html | 4 +-- docs/Rust/section6/associated-generics.html | 4 +-- docs/Rust/section6/generics.html | 4 +-- docs/Rust/section6/lifetimes.html | 4 +-- docs/Rust/section6/traits.html | 4 +-- docs/Rust/section7.html | 4 +-- docs/Rust/section7/closures.html | 4 +-- docs/Rust/section7/iterators.html | 4 +-- docs/Rust/section7/macros.html | 6 ++-- docs/Rust/section8.html | 4 +-- docs/Rust/section8/defining-cargo-config.html | 4 +-- .../section8/defining-crate-features.html | 4 +-- docs/Rust/section8/installing-crate.html | 4 +-- docs/Rust/section8/unit-tests.html | 4 +-- docs/Rust/setup/installation.html | 4 +-- docs/Substrate/section1.html | 4 +-- docs/Substrate/section1/capstone-project.html | 4 +-- docs/Substrate/section1/substrate-design.html | 4 +-- .../Substrate/section1/substrate-history.html | 6 ++-- .../Substrate/section1/what-is-substrate.html | 4 +-- docs/Substrate/section2.html | 4 +-- .../Substrate/section2/substrate-pallets.html | 4 +-- .../Substrate/section2/substrate-runtime.html | 4 +-- .../Substrate/section2/substrate-storage.html | 4 +-- docs/Substrate/section3.html | 4 +-- .../section3/explore-pallet-template.html | 4 +-- docs/Substrate/section3/install-deps.html | 4 +-- .../section3/install-explore-frontend.html | 4 +-- .../section3/node-template-tour.html | 4 +-- docs/Substrate/section4.html | 4 +-- .../section4/create-storage-map.html | 4 +-- docs/Substrate/section4/events-errors.html | 4 +-- docs/Substrate/section4/pallet-config.html | 4 +-- .../Substrate/section4/project-structure.html | 4 +-- docs/Substrate/section5.html | 4 +-- docs/Substrate/section5/coupling-pallets.html | 4 +-- docs/Substrate/section5/dispatchable.html | 6 ++-- docs/Substrate/section5/unit-tests.html | 4 +-- docs/Substrate/section6.html | 4 +-- docs/Substrate/section6/run-node.html | 4 +-- docs/Substrate/section6/test-frontend.html | 4 +-- docs/Substrate/section6/use-polkadotjs.html | 4 +-- docs/Substrate/section7.html | 4 +-- docs/Substrate/section7/blockchain-dev.html | 4 +-- .../Substrate/section7/how-to-test-frame.html | 4 +-- docs/Substrate/section7/runtime-panics.html | 4 +-- docs/Substrate/section8.html | 4 +-- docs/Substrate/section8/benchmarking.html | 4 +-- .../section8/chain-genesis-spec.html | 4 +-- docs/Substrate/section8/origins-calls.html | 4 +-- docs/Substrate/section8/pallet-coupling.html | 4 +-- docs/intro.html | 4 +-- docs/introatoz.html | 4 +-- docs/introblock.html | 4 +-- docs/introdot.html | 4 +-- docs/introparachain.html | 4 +-- docs/introrust.html | 4 +-- docs/introsubstrate.html | 4 +-- docs/introweb3.html | 4 +-- docs/polkadotFAQ.html | 4 +-- index.html | 4 +-- 230 files changed, 493 insertions(+), 496 deletions(-) create mode 100644 assets/js/0939dfa6.2a677a1e.js delete mode 100644 assets/js/0939dfa6.ddb62803.js delete mode 100644 assets/js/093f602f.4093fe70.js create mode 100644 assets/js/093f602f.95aa58b6.js rename assets/js/{0aa99fa2.0f3e17a7.js => 0aa99fa2.624d19e4.js} (56%) delete mode 100644 assets/js/14263310.68ffe623.js create mode 100644 assets/js/14263310.b1464a13.js delete mode 100644 assets/js/47521d56.6e991101.js create mode 100644 assets/js/47521d56.ce090131.js delete mode 100644 assets/js/4a418313.d6dba999.js create mode 100644 assets/js/4a418313.dfac47c1.js create mode 100644 assets/js/4b1e850b.57a51f36.js delete mode 100644 assets/js/4b1e850b.9c39ccf0.js delete mode 100644 assets/js/55a851e8.50ec5685.js create mode 100644 assets/js/55a851e8.5fe8eea0.js delete mode 100644 assets/js/66f9d3f7.5377e133.js create mode 100644 assets/js/66f9d3f7.5c664fe1.js delete mode 100644 assets/js/698a20df.108fd7b0.js create mode 100644 assets/js/698a20df.cb6be3c1.js rename assets/js/{845bb328.d9ddd61b.js => 845bb328.57d0cd9a.js} (66%) delete mode 100644 assets/js/85386164.04e37665.js create mode 100644 assets/js/85386164.3446a23f.js delete mode 100644 assets/js/8568807f.0034759d.js create mode 100644 assets/js/8568807f.bf6000b1.js create mode 100644 assets/js/8a7c7b85.2ea69f52.js delete mode 100644 assets/js/8a7c7b85.77aabbde.js rename assets/js/{9eca237c.7a60832e.js => 9eca237c.eece0504.js} (50%) create mode 100644 assets/js/ddd8c84d.6b6021cc.js delete mode 100644 assets/js/ddd8c84d.73e2251a.js create mode 100644 assets/js/e159a34b.3f058361.js delete mode 100644 assets/js/e159a34b.bc159c19.js create mode 100644 assets/js/fe76b6c3.572fe957.js delete mode 100644 assets/js/fe76b6c3.5f90ddbd.js rename assets/js/{runtime~main.c47f1043.js => runtime~main.dc484ca7.js} (92%) diff --git a/404.html b/404.html index 6c2854a9e..c90b3e966 100644 --- a/404.html +++ b/404.html @@ -5,13 +5,13 @@ Page Not Found | Polkadot Education Initiative - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/assets/js/0939dfa6.2a677a1e.js b/assets/js/0939dfa6.2a677a1e.js new file mode 100644 index 000000000..223350c91 --- /dev/null +++ b/assets/js/0939dfa6.2a677a1e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[9200],{3905:(e,t,a)=>{a.d(t,{Zo:()=>p,kt:()=>h});var r=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function o(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=r.createContext({}),c=function(e){var t=r.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},p=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var a=e.components,n=e.mdxType,i=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(a),d=n,h=u["".concat(l,".").concat(d)]||u[d]||m[d]||i;return a?r.createElement(h,o(o({ref:t},p),{},{components:a})):r.createElement(h,o({ref:t},p))}));function h(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=a.length,o=new Array(i);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:n,o[1]=s;for(var c=2;c{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>m,frontMatter:()=>i,metadata:()=>s,toc:()=>c});var r=a(7462),n=(a(7294),a(3905));const i={id:"macros",title:"Macros in Rust",sidebar_label:"Macros in Rust",description:"Learn what macros are and how to create basic macros in Rust."},o=void 0,s={unversionedId:"Rust/section7/macros",id:"Rust/section7/macros",title:"Macros in Rust",description:"Learn what macros are and how to create basic macros in Rust.",source:"@site/docs/Rust/section7/macros.md",sourceDirName:"Rust/section7",slug:"/Rust/section7/macros",permalink:"/docs/Rust/section7/macros",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Rust/section7/macros.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692960998,formattedLastUpdatedAt:"Aug 25, 2023",frontMatter:{id:"macros",title:"Macros in Rust",sidebar_label:"Macros in Rust",description:"Learn what macros are and how to create basic macros in Rust."},sidebar:"rust",previous:{title:"Closures",permalink:"/docs/Rust/section7/closures"},next:{title:"Learning Cargo, Rust\u2019s Package Management System & Unit Testing",permalink:"/docs/Rust/section8/"}},l={},c=[{value:"Macro Types",id:"macro-types",level:2},{value:"Declarative Macros",id:"declarative-macros",level:3},{value:"Procedural Macros",id:"procedural-macros",level:3},{value:"Writing a Basic Declarative Macro",id:"writing-a-basic-declarative-macro",level:2},{value:"Overview",id:"overview",level:3},{value:"Creating a square! and factor! macro",id:"creating-a-square--and-factor--macro",level:3},{value:"Try it yourself",id:"try-it-yourself",level:2},{value:"What is happening here?",id:"what-is-happening-here",level:2}],p={toc:c},u="wrapper";function m(e){let{components:t,...a}=e;return(0,n.kt)(u,(0,r.Z)({},p,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,'Macros in Rust is, in the most basic sense, "code that writes code", also known as ',(0,n.kt)("em",{parentName:"p"},"metaprogramming"),". By now, you have seen the ",(0,n.kt)("inlineCode",{parentName:"p"},"println!()")," macro many times, and it illustrates how useful macros may be in everyday coding."),(0,n.kt)("p",null,"Another prime example that you have seen is the use of the ",(0,n.kt)("inlineCode",{parentName:"p"},"#[derive]")," macro, which can implement traits on types automatically:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},"// Automatically implements this type.\n#[derive(PartialEq)]\nstruct SomeType;\n")),(0,n.kt)("p",null,'Macros are called before the compiler interprets the code, so they can perform these operations (i.e., implementing a trait for you). This is usually called "expanding," as the macro\'s code expands to actual, usable Rust code the compiler can interpret and use.'),(0,n.kt)("h2",{id:"macro-types"},"Macro Types"),(0,n.kt)("p",null,"There are two primary types of macros:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},'Declarative or "',(0,n.kt)("inlineCode",{parentName:"li"},"macro_rules!"),'" Macros'),(0,n.kt)("li",{parentName:"ul"},"Procedural Macros - which also have subtypes")),(0,n.kt)("h3",{id:"declarative-macros"},"Declarative Macros"),(0,n.kt)("p",null,"Declarative macros are the most widely used and often easier to write than procedural ones. They allow programmers to write expressions akin to ",(0,n.kt)("inlineCode",{parentName:"p"},"match"),' statements that "fill in the blank" to make writing Rust more concise. Put simply; declarative macros operate almost like a template where the parameters provided by the programmer fill in the blanks.'),(0,n.kt)("h3",{id:"procedural-macros"},"Procedural Macros"),(0,n.kt)("p",null,"Procedural macros are more complex, accepting arbitrary code as input and producing code as output. This code, called a ",(0,n.kt)("inlineCode",{parentName:"p"},"TokenStream"),", represents this input and output. Procedural macros operate more like a function, accepting a ",(0,n.kt)("inlineCode",{parentName:"p"},"TokenStream")," as a parameter and specifying a return type as a ",(0,n.kt)("inlineCode",{parentName:"p"},"TokenStream"),". Part of the complexity in creating these macros is that they must be in a separate crate, impacting the Rust project's structure."),(0,n.kt)("p",null,"There are three primary types of procedural macros:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"#[derive]")," macros specify code to add to entities such as structs and enums."),(0,n.kt)("li",{parentName:"ul"},"Function-like macros which structurally look and work like functions."),(0,n.kt)("li",{parentName:"ul"},"Attribute-like macros which define custom attributes on a particular entity.")),(0,n.kt)("p",null,"In this course, we won't be reviewing how to write a procedural macro. For more reading, it is encouraged to read the ",(0,n.kt)("a",{parentName:"p",href:"https://doc.rust-lang.org/book/ch19-06-macros.html"},"Rust Book's examples"),", as well as the ",(0,n.kt)("a",{parentName:"p",href:"https://veykril.github.io/tlborm/"},"The Little Book of Rust Macros")," for more in-depth reading on how macros can be utilized."),(0,n.kt)("h2",{id:"writing-a-basic-declarative-macro"},"Writing a Basic Declarative Macro"),(0,n.kt)("h3",{id:"overview"},"Overview"),(0,n.kt)("p",null,"In this example, we will be writing a declarative macro that utilizes ",(0,n.kt)("inlineCode",{parentName:"p"},"macro_rules!"),". As stated before, a declarative macro similarly works in principle to a match statement, as it declares a set of rules executed in order until the condition is reached. Once the rule is met, the macro generates the corresponding Rust code."),(0,n.kt)("p",null,"Courtesy of ",(0,n.kt)("a",{parentName:"p",href:"https://veykril.github.io/tlborm/decl-macros/macros-methodical.html"},"The Little Book of Rust Macros"),", the following examples help to solidify this concept."),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},"// Each rule looks like the following: \n($matcher) => {$expansion}\n")),(0,n.kt)("p",null,"And in practice:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},'// This simply returns the expression: "4", aka the result of "1 + 3"\nmacro_rules! four {\n () => { 1 + 3 };\n}\n\nfn main() {\n let f = four!(); // 4\n println!("{f}"); // 4\n}\n')),(0,n.kt)("h3",{id:"creating-a-square--and-factor--macro"},"Creating a square! and factor! macro"),(0,n.kt)("p",null,"Macros can also utilize ",(0,n.kt)("a",{parentName:"p",href:"https://veykril.github.io/tlborm/decl-macros/macros-methodical.html#metavariables"},"metavariables")," to capture input and values from outside of the macro. One more commonly used is the ",(0,n.kt)("inlineCode",{parentName:"p"},"expr")," metavariable, which signifies some expression as an input."),(0,n.kt)("p",null,"Using these concepts, let's create a macro that takes a number and squares it:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},'macro_rules! square {\n ($e:expr) => { $e * $e };\n}\n\nfn main() {\n let f = square!(10); // 100\n println!("{f}"); // 100\n}\n')),(0,n.kt)("p",null,"Slightly more advanced, let's allow our macro to take a number, find all factors, then return a new ",(0,n.kt)("inlineCode",{parentName:"p"},"Vec")," of those factors:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},'macro_rules! square {\n ($e:expr) => {\n $e * $e\n };\n}\n\nmacro_rules! find_factors {\n ($e:expr) => {{\n let mut factors = Vec::new();\n for multiplier in 1..=$e {\n if $e % multiplier == 0 {\n factors.push(multiplier);\n }\n }\n factors\n }};\n}\n\nfn main() {\n let f = square!(10);\n let factors = find_factors!(24);\n println!("{f}");\n println!("{:?}", factors);\n}\n')),(0,n.kt)("h2",{id:"try-it-yourself"},"Try it yourself"),(0,n.kt)("iframe",{width:"100%",height:"580",src:"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&code=macro_rules%21+square+%7B%0A++++%28%24e%3Aexpr%29+%3D%3E+%7B%0A++++++++%24e+*+%24e%0A++++%7D%3B%0A%7D%0A%0Amacro_rules%21+find_factors+%7B%0A++++%28%24e%3Aexpr%29+%3D%3E+%7B%7B%0A++++++++let+mut+factors+%3D+Vec%3A%3Anew%28%29%3B%0A++++++++for+multiplier+in+1..%3D%24e+%7B%0A++++++++++++if+%24e+%25+multiplier+%3D%3D+0+%7B%0A++++++++++++++++factors.push%28multiplier%29%3B%0A++++++++++++%7D%0A++++++++%7D%0A++++++++factors%0A++++%7D%7D%3B%0A%7D%0A%0Afn+main%28%29+%7B%0A++++let+f+%3D+square%21%2810%29%3B%0A++++let+factors+%3D+find_factors%21%2824%29%3B%0A++++println%21%28%22%7Bf%7D%22%29%3B%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+factors%29%3B%0A%7D%0A"}),(0,n.kt)("h2",{id:"what-is-happening-here"},"What is happening here?"),(0,n.kt)("p",null,"Two declarative macros are defined, ",(0,n.kt)("inlineCode",{parentName:"p"},"square!")," and ",(0,n.kt)("inlineCode",{parentName:"p"},"find_factors!"),". Both take an expression and return a mutated version of the input. ",(0,n.kt)("inlineCode",{parentName:"p"},"square!")," simply returns a square version of the number, while ",(0,n.kt)("inlineCode",{parentName:"p"},"find_factors!")," does a few novel tasks:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Takes an expression, ",(0,n.kt)("inlineCode",{parentName:"li"},"$e"),"."),(0,n.kt)("li",{parentName:"ul"},"Defines a new inner scope to return."),(0,n.kt)("li",{parentName:"ul"},"Within that scope, creates a ",(0,n.kt)("inlineCode",{parentName:"li"},"Vec")," of factors to return."),(0,n.kt)("li",{parentName:"ul"},"Declares a ",(0,n.kt)("inlineCode",{parentName:"li"},"for")," loop, which iterates from the range of ",(0,n.kt)("inlineCode",{parentName:"li"},"1")," to the value of ",(0,n.kt)("inlineCode",{parentName:"li"},"$e")," (i.e.,. ",(0,n.kt)("inlineCode",{parentName:"li"},"1")," to ",(0,n.kt)("inlineCode",{parentName:"li"},"24"),")."),(0,n.kt)("li",{parentName:"ul"},"Finds if it is a factor via modulo and appends it to the array if it is.")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0939dfa6.ddb62803.js b/assets/js/0939dfa6.ddb62803.js deleted file mode 100644 index efc9d0fa1..000000000 --- a/assets/js/0939dfa6.ddb62803.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[9200],{3905:(e,t,a)=>{a.d(t,{Zo:()=>p,kt:()=>h});var r=a(7294);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function i(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var l=r.createContext({}),c=function(e){var t=r.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},p=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var a=e.components,n=e.mdxType,o=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(a),d=n,h=u["".concat(l,".").concat(d)]||u[d]||m[d]||o;return a?r.createElement(h,i(i({ref:t},p),{},{components:a})):r.createElement(h,i({ref:t},p))}));function h(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=a.length,i=new Array(o);i[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:n,i[1]=s;for(var c=2;c{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var r=a(7462),n=(a(7294),a(3905));const o={id:"macros",title:"Macros in Rust",sidebar_label:"Macros in Rust",description:"Learn what macros are and how to create basic macros in Rust."},i=void 0,s={unversionedId:"Rust/section7/macros",id:"Rust/section7/macros",title:"Macros in Rust",description:"Learn what macros are and how to create basic macros in Rust.",source:"@site/docs/Rust/section7/macros.md",sourceDirName:"Rust/section7",slug:"/Rust/section7/macros",permalink:"/docs/Rust/section7/macros",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Rust/section7/macros.md",tags:[],version:"current",lastUpdatedBy:"Bader Youssef",lastUpdatedAt:1683582484,formattedLastUpdatedAt:"May 8, 2023",frontMatter:{id:"macros",title:"Macros in Rust",sidebar_label:"Macros in Rust",description:"Learn what macros are and how to create basic macros in Rust."},sidebar:"rust",previous:{title:"Closures",permalink:"/docs/Rust/section7/closures"},next:{title:"Learning Cargo, Rust\u2019s Package Management System & Unit Testing",permalink:"/docs/Rust/section8/"}},l={},c=[{value:"Macro Types",id:"macro-types",level:2},{value:"Declarative Macros",id:"declarative-macros",level:3},{value:"Procedural Macros",id:"procedural-macros",level:3},{value:"Writing a Basic Declarative Macro",id:"writing-a-basic-declarative-macro",level:2},{value:"Overview",id:"overview",level:3},{value:"Creating a square! and factor! macro",id:"creating-a-square--and-factor--macro",level:3},{value:"Try it yourself!",id:"try-it-yourself",level:2},{value:"What is happening here?",id:"what-is-happening-here",level:2}],p={toc:c},u="wrapper";function m(e){let{components:t,...a}=e;return(0,n.kt)(u,(0,r.Z)({},p,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,'Macros in Rust is, in the most basic sense, "code that writes code", also known as ',(0,n.kt)("em",{parentName:"p"},"metaprogramming"),". By now, you have seen the ",(0,n.kt)("inlineCode",{parentName:"p"},"println!()")," macro many times, and it illustrates how useful macros may be in everyday coding."),(0,n.kt)("p",null,"Another prime example that you have seen is the use of the ",(0,n.kt)("inlineCode",{parentName:"p"},"#[derive]")," macro, which can implement traits on types automatically: "),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},"// Automatically implements this type.\n#[derive(PartialEq)]\nstruct SomeType;\n")),(0,n.kt)("p",null,'Macros are called before the compiler interprets the code, so they can perform these operations (i.e., implementing a trait for you). This is usually called "expanding," as the macro\'s code expands to actual, usable Rust code the compiler can interpret and use.'),(0,n.kt)("h2",{id:"macro-types"},"Macro Types"),(0,n.kt)("p",null,"There are two primary types of macros:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},'Declarative or "',(0,n.kt)("inlineCode",{parentName:"li"},"macro_rules!"),'" Macros'),(0,n.kt)("li",{parentName:"ul"},"Procedural Macros - which also have subtypes")),(0,n.kt)("h3",{id:"declarative-macros"},"Declarative Macros"),(0,n.kt)("p",null,"Declarative macros are the most widely used and often easier to write than procedural ones. They allow programmers to write expressions akin to ",(0,n.kt)("inlineCode",{parentName:"p"},"match"),' statements that "fill in the blank" to make writing Rust more concise. Put simply; declarative macros operate almost like a template where the parameters provided by the programmer fill in the blanks.'),(0,n.kt)("h3",{id:"procedural-macros"},"Procedural Macros"),(0,n.kt)("p",null,"Procedural macros are more complex, accepting arbitrary code as input and producing code as output. This code, called a ",(0,n.kt)("inlineCode",{parentName:"p"},"TokenStream"),", represents this input and output. Procedural macros operate more like a function, accepting a ",(0,n.kt)("inlineCode",{parentName:"p"},"TokenStream")," as a parameter and specifying a return type as a ",(0,n.kt)("inlineCode",{parentName:"p"},"TokenStream"),". Part of the complexity in creating these macros is that they must be in a separate crate, impacting the Rust project's structure."),(0,n.kt)("p",null,"There are three primary types of procedural macros:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"#[derive]")," macros specify code to add to entities such as structs and enums."),(0,n.kt)("li",{parentName:"ul"},"Function-like macros which structurally look and work like functions."),(0,n.kt)("li",{parentName:"ul"},"Attribute-like macros which define custom attributes on a particular entity.")),(0,n.kt)("p",null,"In this course, we won't be reviewing how to write a procedural macro. For more reading, it is encouraged to read the ",(0,n.kt)("a",{parentName:"p",href:"https://doc.rust-lang.org/book/ch19-06-macros.html"},"Rust Book's examples"),", as well as the ",(0,n.kt)("a",{parentName:"p",href:"https://veykril.github.io/tlborm/"},"The Little Book of Rust Macros")," for more in-depth reading on how macros can be utilized. "),(0,n.kt)("h2",{id:"writing-a-basic-declarative-macro"},"Writing a Basic Declarative Macro"),(0,n.kt)("h3",{id:"overview"},"Overview"),(0,n.kt)("p",null,"In this example, we will be writing a declarative macro that utilizes ",(0,n.kt)("inlineCode",{parentName:"p"},"macro_rules!"),". As stated before, a declarative macro similarly works in principle to a match statement, as it declares a set of rules executed in order until the condition is reached. Once the rule is met, the macro generates the corresponding Rust code."),(0,n.kt)("p",null,"Courtesy of ",(0,n.kt)("a",{parentName:"p",href:"https://veykril.github.io/tlborm/decl-macros/macros-methodical.html"},"The Little Book of Rust Macros"),", the following examples help to solidify this concept."),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},"// Each rule looks like the following: \n($matcher) => {$expansion}\n")),(0,n.kt)("p",null,"And in practice: "),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},'// This simply returns the expression: "4", aka the result of "1 + 3"\nmacro_rules! four {\n () => { 1 + 3 };\n}\n\nfn main() {\n let f = four!(); // 4\n println!("{f}"); // 4\n}\n')),(0,n.kt)("h3",{id:"creating-a-square--and-factor--macro"},"Creating a square! and factor! macro"),(0,n.kt)("p",null,"Macros can also utilize ",(0,n.kt)("a",{parentName:"p",href:"https://veykril.github.io/tlborm/decl-macros/macros-methodical.html#metavariables"},"metavariables")," to capture input and values from outside of the macro. One more commonly used is the ",(0,n.kt)("inlineCode",{parentName:"p"},"expr")," metavariable, which signifies some expression as an input."),(0,n.kt)("p",null,"Using these concepts, let's create a macro that takes a number and squares it: "),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},'macro_rules! square {\n ($e:expr) => { $e * $e };\n}\n\nfn main() {\n let f = square!(10); // 100\n println!("{f}"); // 100\n}\n')),(0,n.kt)("p",null,"Slightly more advanced, let's allow our macro to take a number, find all factors, then return a new ",(0,n.kt)("inlineCode",{parentName:"p"},"Vec")," of those factors:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},'macro_rules! square {\n ($e:expr) => {\n $e * $e\n };\n}\n\nmacro_rules! find_factors {\n ($e:expr) => {{\n let mut factors = Vec::new();\n for multipler in 1..=$e {\n if $e % multipler == 0 {\n factors.push(multipler);\n }\n }\n factors\n }};\n}\n\nfn main() {\n let f = square!(10);\n let factors = find_factors!(24);\n println!("{f}");\n println!("{:?}", factors);\n}\n')),(0,n.kt)("h2",{id:"try-it-yourself"},"Try it yourself!"),(0,n.kt)("iframe",{width:"100%",height:"580",src:"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&code=macro_rules%21+square+%7B%0A++++%28%24e%3Aexpr%29+%3D%3E+%7B%0A++++++++%24e+*+%24e%0A++++%7D%3B%0A%7D%0A%0Amacro_rules%21+find_factors+%7B%0A++++%28%24e%3Aexpr%29+%3D%3E+%7B%7B%0A++++++++let+mut+factors+%3D+Vec%3A%3Anew%28%29%3B%0A++++++++for+multipler+in+1..%3D%24e+%7B%0A++++++++++++if+%24e+%25+multipler+%3D%3D+0+%7B%0A++++++++++++++++factors.push%28multipler%29%3B%0A++++++++++++%7D%0A++++++++%7D%0A++++++++factors%0A++++%7D%7D%3B%0A%7D%0A%0Afn+main%28%29+%7B%0A++++let+f+%3D+square%21%2810%29%3B%0A++++let+factors+%3D+find_factors%21%2824%29%3B%0A++++println%21%28%22%7Bf%7D%22%29%3B%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+factors%29%3B%0A%7D%0A"}),(0,n.kt)("h2",{id:"what-is-happening-here"},"What is happening here?"),(0,n.kt)("p",null,"Two declarative macros are defined, ",(0,n.kt)("inlineCode",{parentName:"p"},"square!")," and ",(0,n.kt)("inlineCode",{parentName:"p"},"find_factors!"),". Both take an expression and return a mutated version of the input. ",(0,n.kt)("inlineCode",{parentName:"p"},"square!")," simply returns a square version of the number, while ",(0,n.kt)("inlineCode",{parentName:"p"},"find_factors!")," does a few novel tasks: "),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Takes an expression, ",(0,n.kt)("inlineCode",{parentName:"li"},"$e"),"."),(0,n.kt)("li",{parentName:"ul"},"Defines a new inner scope to return."),(0,n.kt)("li",{parentName:"ul"},"Within that scope, creates a ",(0,n.kt)("inlineCode",{parentName:"li"},"Vec")," of factors to return."),(0,n.kt)("li",{parentName:"ul"},"Declares a ",(0,n.kt)("inlineCode",{parentName:"li"},"for")," loop, which iterates from the range of ",(0,n.kt)("inlineCode",{parentName:"li"},"1")," to the value of ",(0,n.kt)("inlineCode",{parentName:"li"},"$e")," (i.e.,. ",(0,n.kt)("inlineCode",{parentName:"li"},"1")," to ",(0,n.kt)("inlineCode",{parentName:"li"},"24"),")."),(0,n.kt)("li",{parentName:"ul"},"Finds if it is a factor via modulo and appends it to the array if it is.")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/093f602f.4093fe70.js b/assets/js/093f602f.4093fe70.js deleted file mode 100644 index 1dff98b63..000000000 --- a/assets/js/093f602f.4093fe70.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[5599],{3905:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>m});var a=t(7294);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function i(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var l=a.createContext({}),d=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},u=function(e){var n=d(e.components);return a.createElement(l.Provider,{value:n},e.children)},c="mdxType",p={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},h=a.forwardRef((function(e,n){var t=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),c=d(t),h=r,m=c["".concat(l,".").concat(h)]||c[h]||p[h]||o;return t?a.createElement(m,i(i({ref:n},u),{},{components:t})):a.createElement(m,i({ref:n},u))}));function m(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var o=t.length,i=new Array(o);i[0]=h;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[c]="string"==typeof e?e:r,i[1]=s;for(var d=2;d{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>o,metadata:()=>s,toc:()=>d});var a=t(7462),r=(t(7294),t(3905));const o={id:"dispatchable",title:"Creating dispatchable functions",sidebar_label:"Creating dispatchable functions",description:"Creating dispatchable functions for our pallet."},i=void 0,s={unversionedId:"Substrate/section5/dispatchable",id:"Substrate/section5/dispatchable",title:"Creating dispatchable functions",description:"Creating dispatchable functions for our pallet.",source:"@site/docs/Substrate/section5/dispatchable.md",sourceDirName:"Substrate/section5",slug:"/Substrate/section5/dispatchable",permalink:"/docs/Substrate/section5/dispatchable",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section5/dispatchable.md",tags:[],version:"current",lastUpdatedBy:"Bader Youssef",lastUpdatedAt:1687791313,formattedLastUpdatedAt:"Jun 26, 2023",frontMatter:{id:"dispatchable",title:"Creating dispatchable functions",sidebar_label:"Creating dispatchable functions",description:"Creating dispatchable functions for our pallet."},sidebar:"substrate",previous:{title:"Building a FRAME Pallet",permalink:"/docs/Substrate/section5/"},next:{title:"Writing unit tests for pallets",permalink:"/docs/Substrate/section5/unit-tests"}},l={},d=[{value:"Adding the register dispatchable",id:"adding-the-register-dispatchable",level:2},{value:"Defining our sender and function",id:"defining-our-sender-and-function",level:3},{value:"Checking balance and using ensure! to check requirements",id:"checking-balance-and-using-ensure-to-check-requirements",level:3},{value:"Unbounded to bounded",id:"unbounded-to-bounded",level:3},{value:"Generate a gradient profile and build our user",id:"generate-a-gradient-profile-and-build-our-user",level:3},{value:"Lock balance and store our user",id:"lock-balance-and-store-our-user",level:3},{value:"Update the total amount of users on the network",id:"update-the-total-amount-of-users-on-the-network",level:3},{value:"Emit an event",id:"emit-an-event",level:3},{value:"Full register function",id:"full-register-function",level:3}],u={toc:d},c="wrapper";function p(e){let{components:n,...t}=e;return(0,r.kt)(c,(0,a.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"With all of our tools in place to register a user, let's go through the process of adequately registering them via an ",(0,r.kt)("strong",{parentName:"p"},"extrinsic"),"or state change."),(0,r.kt)("admonition",{title:"What is an extrinsic again?",type:"info"},(0,r.kt)("p",{parentName:"admonition"},"An extrinsic is a transaction or a unit to define how the state should change within the network.")),(0,r.kt)("h2",{id:"adding-the-register-dispatchable"},"Adding the ",(0,r.kt)("inlineCode",{parentName:"h2"},"register")," dispatchable"),(0,r.kt)("p",null,"Navigate to the ",(0,r.kt)("inlineCode",{parentName:"p"},"#[pallet::call]")," macro; it should be empty:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"#[pallet::call]\nimpl Pallet {\n // Our dispatchable goes here.\n}\n")),(0,r.kt)("p",null,"We are going to create a function called ",(0,r.kt)("inlineCode",{parentName:"p"},"register"),", which will take several parameters/factors into consideration and perform the following checks:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Take a parameter, ",(0,r.kt)("inlineCode",{parentName:"li"},"name"),", of the user. The character amount must be below ",(0,r.kt)("inlineCode",{parentName:"li"},"MaxNameLength"),"."),(0,r.kt)("li",{parentName:"ul"},"Take a parameter, ",(0,r.kt)("inlineCode",{parentName:"li"},"bio"),", of the user. The character amount must be below ",(0,r.kt)("inlineCode",{parentName:"li"},"MaxBioLength"),"."),(0,r.kt)("li",{parentName:"ul"},"Check if they have enough balance to lock, and if so, lock it. Else, they cannot register."),(0,r.kt)("li",{parentName:"ul"},"Generate a profile picture for our user"),(0,r.kt)("li",{parentName:"ul"},"If the user meets the requirements, we store them in our ",(0,r.kt)("inlineCode",{parentName:"li"},"StorageMap"),"."),(0,r.kt)("li",{parentName:"ul"},"Emit an event that they registered. ")),(0,r.kt)("h3",{id:"defining-our-sender-and-function"},"Defining our sender and function"),(0,r.kt)("p",null,"With our requirements adequately defined, we can begin coding this function. Go ahead and paste the function called ",(0,r.kt)("inlineCode",{parentName:"p"},"register")," that includes some beginning logic to start:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"#[pallet::call]\nimpl Pallet {\n\n /// Registers a user to the network. It requires the balance of the sender to have an amount\n /// which is greater than or equal to MinimumLockableAmount. Locks MinimumLockableAmount as\n /// part of the registration process.\n #[pallet::call_index(0)]\n pub fn register(origin: OriginFor, name: Vec, bio: Vec) -> DispatchResult {\n let sender = ensure_signed(origin)?;\n }\n}\n")),(0,r.kt)("h3",{id:"checking-balance-and-using-ensure-to-check-requirements"},"Checking balance and using ",(0,r.kt)("inlineCode",{parentName:"h3"},"ensure!")," to check requirements"),(0,r.kt)("p",null,"A very useful macro, ",(0,r.kt)("inlineCode",{parentName:"p"},"ensure!"),", is provided by FRAME. This macro enables us to check a condition. If the condition proves false, it allows an extrinsic to fail with a specific error. "),(0,r.kt)("p",null,"We can also use the ",(0,r.kt)("inlineCode",{parentName:"p"},"Currency")," trait included with our configuration. We will elaborate more on this trait later but know that for now, it enables us to check the balance of the sender:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'// Retrieve the "free" balance of the user\nlet balance = T::Currency::free_balance(&sender);\n\n// Before proceeding - we have to make sure the *free* balance of a user is enough to\n// lock up! Otherwise, we halt this dispatchable with an error.\nensure!(balance >= T::MinimumLockableAmount::get(), Error::::LowBalance);\n')),(0,r.kt)("h3",{id:"unbounded-to-bounded"},"Unbounded to bounded"),(0,r.kt)("p",null,"If you notice, the parameters (",(0,r.kt)("inlineCode",{parentName:"p"},"name")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"bio"),") provided in ",(0,r.kt)("inlineCode",{parentName:"p"},"register")," are of type ",(0,r.kt)("inlineCode",{parentName:"p"},"Vec"),". This is a vector of bytes, which you may now consider a ",(0,r.kt)("inlineCode",{parentName:"p"},"String"),". "),(0,r.kt)("p",null,"Within our config, we have two notable constants defined: ",(0,r.kt)("inlineCode",{parentName:"p"},"MaxBioLength")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"MaxNameLength"),". We want our two parameters to be ",(0,r.kt)("strong",{parentName:"p"},"bounded")," to these limits, as we shouldn't allow for infinite values to be stored on the chain."),(0,r.kt)("p",null,"The following code does just that and maps the appropriate error if it does exceed this length. We use the type ",(0,r.kt)("inlineCode",{parentName:"p"},"BoundedVec")," to convert from a ",(0,r.kt)("inlineCode",{parentName:"p"},"Vec")," to something that is bounded:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"let name_bounded: BoundedVec =\n BoundedVec::try_from(name.clone()).map_err(|_| Error::::NameTooLong)?;\nlet bio_bounded: BoundedVec =\n BoundedVec::try_from(bio).map_err(|_| Error::::BioTooLong)?;\n\n// 2. Check if the name already exists or user metadata already exists\nensure!(>::get(&name_bounded).is_none(), Error::::NameInUse);\nensure!(\n >::get(&sender).is_none(),\n Error::::AccountIdAlreadyRegistered\n); \n")),(0,r.kt)("h3",{id:"generate-a-gradient-profile-and-build-our-user"},"Generate a gradient profile and build our user"),(0,r.kt)("p",null,"We can build our user once we have our parameters converted and ready."),(0,r.kt)("p",null,"Firstly, we can call another trait, ",(0,r.kt)("inlineCode",{parentName:"p"},"T::Randomness"),", to provide a random value to the included ",(0,r.kt)("inlineCode",{parentName:"p"},"generate_hex_values")," function. "),(0,r.kt)("p",null,"This will return two randomly generated hex values that can be used to create a gradient profile picture:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// Generate our random profile picture (aka, two hex values that form a gradient)\n// Usually, some increasing nonce is used as a seed. For simplicity, we use the account\n// id as the seed.\nlet (value, _) = T::Randomness::random(&sender.encode());\nlet random_pfp = Self::generate_hex_values(value);\n\n// Construct our UserMetadata. Ideally, we could also create an implementation to make\n// this easier to create!\nlet user_metadata: UserMetadata = UserMetadata {\n name: name_bounded.clone(),\n bio: bio_bounded,\n profile_gradient: random_pfp,\n account_id: sender.clone(),\n};\n")),(0,r.kt)("h3",{id:"lock-balance-and-store-our-user"},"Lock balance and store our user"),(0,r.kt)("p",null,"With our users fully configured, we can now lock their balance and finish the registration process by storing them in our ",(0,r.kt)("inlineCode",{parentName:"p"},"RegisteredUsers")," mapping:"),(0,r.kt)("p",null,"We also add the user's name to another mapping of names (",(0,r.kt)("inlineCode",{parentName:"p"},"Names"),") to ensure it doesn't get taken."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// Lock the minimum deposit. This account will now have this amount locked until\n// they 'de-register'.\nT::Currency::set_lock(\n LOCK_ID,\n &sender,\n T::MinimumLockableAmount::get(),\n WithdrawReasons::RESERVE,\n);\n\n// Store the user, add to existing names, and update the total amount of users\n>::insert(&sender, user_metadata);\n>::insert(&name_bounded, sender.clone());\n")),(0,r.kt)("h3",{id:"update-the-total-amount-of-users-on-the-network"},"Update the total amount of users on the network"),(0,r.kt)("p",null,"Once we register the user, we can update the total number of users on the network. Note the use of ",(0,r.kt)("inlineCode",{parentName:"p"},"checked_add")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"unwrap_or_default()"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// Note the use of 'unwrap_or_default' - this is better than just a plain 'unwrap()'\n// The default for 'u32' is 0, meaning an 'unwrap_or(0)' could also work here!\n\nlet total_registered = >::get().unwrap_or_default();\n\n// Using checked_add() ensures 'safe math' occurs.\n// Since we never want panic within a runtime, we have to ensure all *possible* errors\n// can be caught.\n\n>::put(\n total_registered.checked_add(1).ok_or(Error::::IntegerOverflow)?,\n);\n")),(0,r.kt)("h3",{id:"emit-an-event"},"Emit an event"),(0,r.kt)("p",null,"Lastly, we can emit an event once everything above is performed to indicate a new user has been registered:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// Emit an event to indicate a new user was added to the network\nSelf::deposit_event(Event::Registered { id: sender });\n")),(0,r.kt)("h3",{id:"full-register-function"},"Full ",(0,r.kt)("inlineCode",{parentName:"h3"},"register")," function"),(0,r.kt)("p",null,"The entire register function should end up looking like this by the end:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[pallet::call_index(0)]\n pub fn register(origin: OriginFor, name: Vec, bio: Vec) -> DispatchResult {\n let sender = ensure_signed(origin)?;\n let balance = T::Currency::free_balance(&sender);\n\n // Before proceeding - we have to make sure the *free* balance of a user is enough to\n // lock up! Otherwise, we halt this dispatchable with an error.\n ensure!(balance >= T::MinimumLockableAmount::get(), Error::::LowBalance);\n\n // 1. Craft the user metadata out of the given parameters from `register`.\n // Keep in mind we have to cast these to `BoundedVec` using the limits we have defined\n // in our Config (hence why we must access them using our handy `T` generic operator!).\n // Notice the error handling! Other types of error handling are okay too :)\n\n let name_bounded: BoundedVec =\n BoundedVec::try_from(name.clone()).map_err(|_| Error::::NameTooLong)?;\n let bio_bounded: BoundedVec =\n BoundedVec::try_from(bio).map_err(|_| Error::::BioTooLong)?;\n\n // 2. Check if the name already exists or user metadata already exists\n ensure!(>::get(&name_bounded).is_none(), Error::::NameInUse);\n ensure!(\n >::get(&sender).is_none(),\n Error::::AccountIdAlreadyRegistered\n );\n\n // 3. Generate our random profile picture (aka, two hex values which form a gradient)\n // Usually, some increasing nonce is used as a seed. For simplicity, we use the account\n // id as the seed.\n let (value, _) = T::Randomness::random(&sender.encode());\n let random_pfp = Self::generate_hex_values(value);\n\n // 4. Construct our UserMetadata. Ideally, we could also create an implemention to make\n // this easier to create!\n let user_metadata: UserMetadata = UserMetadata {\n name: name_bounded.clone(),\n bio: bio_bounded,\n profile_gradient: random_pfp,\n account_id: sender.clone(),\n };\n\n // 5. Lock the minimum deposit. This account will now have this amount locked until\n // they 'de-register'.\n T::Currency::set_lock(\n LOCK_ID,\n &sender,\n T::MinimumLockableAmount::get(),\n WithdrawReasons::RESERVE,\n );\n\n // 6. Store the user, add to existing names, and update total amount of users\n >::insert(&sender, user_metadata);\n >::insert(&name_bounded, sender.clone());\n\n // Note the use of 'unwrap_or_default' - this is better than just a plain 'unwrap()'\n // The default for 'u32' is 0, meaning an 'unwrap_or(0)' could also work here!\n\n let total_registered = >::get().unwrap_or_default();\n\n // The use of checked_add() ensures 'safe math' is taking place.\n // Since we never want panic within a runtime, we have to ensure all *possible* errors\n // can be caught.\n\n >::put(\n total_registered.checked_add(1).ok_or(Error::::IntegerOverflow)?,\n );\n\n // 7. Emit an event to indicate a new user was added to the network\n Self::deposit_event(Event::Registered { id: sender });\n\n Ok(())\n }\n")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/093f602f.95aa58b6.js b/assets/js/093f602f.95aa58b6.js new file mode 100644 index 000000000..3190b0852 --- /dev/null +++ b/assets/js/093f602f.95aa58b6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[5599],{3905:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>m});var a=t(7294);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function i(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var l=a.createContext({}),d=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},u=function(e){var n=d(e.components);return a.createElement(l.Provider,{value:n},e.children)},c="mdxType",p={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},h=a.forwardRef((function(e,n){var t=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),c=d(t),h=r,m=c["".concat(l,".").concat(h)]||c[h]||p[h]||o;return t?a.createElement(m,i(i({ref:n},u),{},{components:t})):a.createElement(m,i({ref:n},u))}));function m(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var o=t.length,i=new Array(o);i[0]=h;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s[c]="string"==typeof e?e:r,i[1]=s;for(var d=2;d{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>o,metadata:()=>s,toc:()=>d});var a=t(7462),r=(t(7294),t(3905));const o={id:"dispatchable",title:"Creating dispatchable functions",sidebar_label:"Creating dispatchable functions",description:"Creating dispatchable functions for our pallet."},i=void 0,s={unversionedId:"Substrate/section5/dispatchable",id:"Substrate/section5/dispatchable",title:"Creating dispatchable functions",description:"Creating dispatchable functions for our pallet.",source:"@site/docs/Substrate/section5/dispatchable.md",sourceDirName:"Substrate/section5",slug:"/Substrate/section5/dispatchable",permalink:"/docs/Substrate/section5/dispatchable",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section5/dispatchable.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692960998,formattedLastUpdatedAt:"Aug 25, 2023",frontMatter:{id:"dispatchable",title:"Creating dispatchable functions",sidebar_label:"Creating dispatchable functions",description:"Creating dispatchable functions for our pallet."},sidebar:"substrate",previous:{title:"Building a FRAME Pallet",permalink:"/docs/Substrate/section5/"},next:{title:"Writing unit tests for pallets",permalink:"/docs/Substrate/section5/unit-tests"}},l={},d=[{value:"Adding the register dispatchable",id:"adding-the-register-dispatchable",level:2},{value:"Defining our sender and function",id:"defining-our-sender-and-function",level:3},{value:"Checking balance and using ensure! to check requirements",id:"checking-balance-and-using-ensure-to-check-requirements",level:3},{value:"Unbounded to bounded",id:"unbounded-to-bounded",level:3},{value:"Generate a gradient profile and build our user",id:"generate-a-gradient-profile-and-build-our-user",level:3},{value:"Lock balance and store our user",id:"lock-balance-and-store-our-user",level:3},{value:"Update the total amount of users on the network",id:"update-the-total-amount-of-users-on-the-network",level:3},{value:"Emit an event",id:"emit-an-event",level:3},{value:"Full register function",id:"full-register-function",level:3}],u={toc:d},c="wrapper";function p(e){let{components:n,...t}=e;return(0,r.kt)(c,(0,a.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"With all of our tools in place to register a user, let's go through the process of adequately registering them via an ",(0,r.kt)("strong",{parentName:"p"},"extrinsic"),"or state change."),(0,r.kt)("admonition",{title:"What is an extrinsic again?",type:"info"},(0,r.kt)("p",{parentName:"admonition"},"An extrinsic is a transaction or a unit to define how the state should change within the network.")),(0,r.kt)("h2",{id:"adding-the-register-dispatchable"},"Adding the ",(0,r.kt)("inlineCode",{parentName:"h2"},"register")," dispatchable"),(0,r.kt)("p",null,"Navigate to the ",(0,r.kt)("inlineCode",{parentName:"p"},"#[pallet::call]")," macro; it should be empty:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"#[pallet::call]\nimpl Pallet {\n // Our dispatchable goes here.\n}\n")),(0,r.kt)("p",null,"We are going to create a function called ",(0,r.kt)("inlineCode",{parentName:"p"},"register"),", which will take several parameters/factors into consideration and perform the following checks:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Take a parameter, ",(0,r.kt)("inlineCode",{parentName:"li"},"name"),", of the user. The character amount must be below ",(0,r.kt)("inlineCode",{parentName:"li"},"MaxNameLength"),"."),(0,r.kt)("li",{parentName:"ul"},"Take a parameter, ",(0,r.kt)("inlineCode",{parentName:"li"},"bio"),", of the user. The character amount must be below ",(0,r.kt)("inlineCode",{parentName:"li"},"MaxBioLength"),"."),(0,r.kt)("li",{parentName:"ul"},"Check if they have enough balance to lock, and if so, lock it. Else, they cannot register."),(0,r.kt)("li",{parentName:"ul"},"Generate a profile picture for our user"),(0,r.kt)("li",{parentName:"ul"},"If the user meets the requirements, we store them in our ",(0,r.kt)("inlineCode",{parentName:"li"},"StorageMap"),"."),(0,r.kt)("li",{parentName:"ul"},"Emit an event that they registered.")),(0,r.kt)("h3",{id:"defining-our-sender-and-function"},"Defining our sender and function"),(0,r.kt)("p",null,"With our requirements adequately defined, we can begin coding this function. Go ahead and paste the function called ",(0,r.kt)("inlineCode",{parentName:"p"},"register")," that includes some beginning logic to start:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"#[pallet::call]\nimpl Pallet {\n\n /// Registers a user to the network. It requires the balance of the sender to have an amount\n /// which is greater than or equal to MinimumLockableAmount. Locks MinimumLockableAmount as\n /// part of the registration process.\n #[pallet::call_index(0)]\n pub fn register(origin: OriginFor, name: Vec, bio: Vec) -> DispatchResult {\n let sender = ensure_signed(origin)?;\n }\n}\n")),(0,r.kt)("h3",{id:"checking-balance-and-using-ensure-to-check-requirements"},"Checking balance and using ",(0,r.kt)("inlineCode",{parentName:"h3"},"ensure!")," to check requirements"),(0,r.kt)("p",null,"A very useful macro, ",(0,r.kt)("inlineCode",{parentName:"p"},"ensure!"),", is provided by FRAME. This macro enables us to check a condition. If the condition proves false, it allows an extrinsic to fail with a specific error. "),(0,r.kt)("p",null,"We can also use the ",(0,r.kt)("inlineCode",{parentName:"p"},"Currency")," trait included with our configuration. We will elaborate more on this trait later but know that for now, it enables us to check the balance of the sender:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'// Retrieve the "free" balance of the user\nlet balance = T::Currency::free_balance(&sender);\n\n// Before proceeding - we have to make sure the *free* balance of a user is enough to\n// lock up! Otherwise, we halt this dispatchable with an error.\nensure!(balance >= T::MinimumLockableAmount::get(), Error::::LowBalance);\n')),(0,r.kt)("h3",{id:"unbounded-to-bounded"},"Unbounded to bounded"),(0,r.kt)("p",null,"If you notice, the parameters (",(0,r.kt)("inlineCode",{parentName:"p"},"name")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"bio"),") provided in ",(0,r.kt)("inlineCode",{parentName:"p"},"register")," are of type ",(0,r.kt)("inlineCode",{parentName:"p"},"Vec"),". This is a vector of bytes, which you may now consider a ",(0,r.kt)("inlineCode",{parentName:"p"},"String"),". "),(0,r.kt)("p",null,"Within our config, we have two notable constants defined: ",(0,r.kt)("inlineCode",{parentName:"p"},"MaxBioLength")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"MaxNameLength"),". We want our two parameters to be ",(0,r.kt)("strong",{parentName:"p"},"bounded")," to these limits, as we shouldn't allow for infinite values to be stored on the chain."),(0,r.kt)("p",null,"The following code does just that and maps the appropriate error if it does exceed this length. We use the type ",(0,r.kt)("inlineCode",{parentName:"p"},"BoundedVec")," to convert from a ",(0,r.kt)("inlineCode",{parentName:"p"},"Vec")," to something that is bounded:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"let name_bounded: BoundedVec =\n BoundedVec::try_from(name.clone()).map_err(|_| Error::::NameTooLong)?;\nlet bio_bounded: BoundedVec =\n BoundedVec::try_from(bio).map_err(|_| Error::::BioTooLong)?;\n\n// 2. Check if the name already exists or user metadata already exists\nensure!(>::get(&name_bounded).is_none(), Error::::NameInUse);\nensure!(\n >::get(&sender).is_none(),\n Error::::AccountIdAlreadyRegistered\n); \n")),(0,r.kt)("h3",{id:"generate-a-gradient-profile-and-build-our-user"},"Generate a gradient profile and build our user"),(0,r.kt)("p",null,"We can build our user once we have our parameters converted and ready."),(0,r.kt)("p",null,"Firstly, we can call another trait, ",(0,r.kt)("inlineCode",{parentName:"p"},"T::Randomness"),", to provide a random value to the included ",(0,r.kt)("inlineCode",{parentName:"p"},"generate_hex_values")," function. "),(0,r.kt)("p",null,"This will return two randomly generated hex values that can be used to create a gradient profile picture:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// Generate our random profile picture (aka, two hex values that form a gradient)\n// Usually, some increasing nonce is used as a seed. For simplicity, we use the account\n// id as the seed.\nlet (value, _) = T::Randomness::random(&sender.encode());\nlet random_pfp = Self::generate_hex_values(value);\n\n// Construct our UserMetadata. Ideally, we could also create an implementation to make\n// this easier to create!\nlet user_metadata: UserMetadata = UserMetadata {\n name: name_bounded.clone(),\n bio: bio_bounded,\n profile_gradient: random_pfp,\n account_id: sender.clone(),\n};\n")),(0,r.kt)("h3",{id:"lock-balance-and-store-our-user"},"Lock balance and store our user"),(0,r.kt)("p",null,"With our users fully configured, we can now lock their balance and finish the registration process by storing them in our ",(0,r.kt)("inlineCode",{parentName:"p"},"RegisteredUsers")," mapping:"),(0,r.kt)("p",null,"We also add the user's name to another mapping of names (",(0,r.kt)("inlineCode",{parentName:"p"},"Names"),") to ensure it doesn't get taken."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// Lock the minimum deposit. This account will now have this amount locked until\n// they 'de-register'.\nT::Currency::set_lock(\n LOCK_ID,\n &sender,\n T::MinimumLockableAmount::get(),\n WithdrawReasons::RESERVE,\n);\n\n// Store the user, add to existing names, and update the total amount of users\n>::insert(&sender, user_metadata);\n>::insert(&name_bounded, sender.clone());\n")),(0,r.kt)("h3",{id:"update-the-total-amount-of-users-on-the-network"},"Update the total amount of users on the network"),(0,r.kt)("p",null,"Once we register the user, we can update the total number of users on the network. Note the use of ",(0,r.kt)("inlineCode",{parentName:"p"},"checked_add")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"unwrap_or_default()"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// Note the use of 'unwrap_or_default' - this is better than just a plain 'unwrap()'\n// The default for 'u32' is 0, meaning an 'unwrap_or(0)' could also work here!\n\nlet total_registered = >::get().unwrap_or_default();\n\n// Using checked_add() ensures 'safe math' occurs.\n// Since we never want panic within a runtime, we have to ensure all *possible* errors\n// can be caught.\n\n>::put(\n total_registered.checked_add(1).ok_or(Error::::IntegerOverflow)?,\n);\n")),(0,r.kt)("h3",{id:"emit-an-event"},"Emit an event"),(0,r.kt)("p",null,"Lastly, we can emit an event once everything above is performed to indicate a new user has been registered:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// Emit an event to indicate a new user was added to the network\nSelf::deposit_event(Event::Registered { id: sender });\n")),(0,r.kt)("h3",{id:"full-register-function"},"Full ",(0,r.kt)("inlineCode",{parentName:"h3"},"register")," function"),(0,r.kt)("p",null,"The entire register function should end up looking like this by the end:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[pallet::call_index(0)]\n pub fn register(origin: OriginFor, name: Vec, bio: Vec) -> DispatchResult {\n let sender = ensure_signed(origin)?;\n let balance = T::Currency::free_balance(&sender);\n\n // Before proceeding - we have to make sure the *free* balance of a user is enough to\n // lock up! Otherwise, we halt this dispatchable with an error.\n ensure!(balance >= T::MinimumLockableAmount::get(), Error::::LowBalance);\n\n // 1. Craft the user metadata out of the given parameters from `register`.\n // Keep in mind we have to cast these to `BoundedVec` using the limits we have defined\n // in our Config (hence why we must access them using our handy `T` generic operator!).\n // Notice the error handling! Other types of error handling are okay too :)\n\n let name_bounded: BoundedVec =\n BoundedVec::try_from(name.clone()).map_err(|_| Error::::NameTooLong)?;\n let bio_bounded: BoundedVec =\n BoundedVec::try_from(bio).map_err(|_| Error::::BioTooLong)?;\n\n // 2. Check if the name already exists or user metadata already exists\n ensure!(>::get(&name_bounded).is_none(), Error::::NameInUse);\n ensure!(\n >::get(&sender).is_none(),\n Error::::AccountIdAlreadyRegistered\n );\n\n // 3. Generate our random profile picture (aka, two hex values which form a gradient)\n // Usually, some increasing nonce is used as a seed. For simplicity, we use the account\n // id as the seed.\n let (value, _) = T::Randomness::random(&sender.encode());\n let random_pfp = Self::generate_hex_values(value);\n\n // 4. Construct our UserMetadata. Ideally, we could also create an implementation to make\n // this easier to create!\n let user_metadata: UserMetadata = UserMetadata {\n name: name_bounded.clone(),\n bio: bio_bounded,\n profile_gradient: random_pfp,\n account_id: sender.clone(),\n };\n\n // 5. Lock the minimum deposit. This account will now have this amount locked until\n // they 'de-register'.\n T::Currency::set_lock(\n LOCK_ID,\n &sender,\n T::MinimumLockableAmount::get(),\n WithdrawReasons::RESERVE,\n );\n\n // 6. Store the user, add to existing names, and update total amount of users\n >::insert(&sender, user_metadata);\n >::insert(&name_bounded, sender.clone());\n\n // Note the use of 'unwrap_or_default' - this is better than just a plain 'unwrap()'\n // The default for 'u32' is 0, meaning an 'unwrap_or(0)' could also work here!\n\n let total_registered = >::get().unwrap_or_default();\n\n // The use of checked_add() ensures 'safe math' is taking place.\n // Since we never want panic within a runtime, we have to ensure all *possible* errors\n // can be caught.\n\n >::put(\n total_registered.checked_add(1).ok_or(Error::::IntegerOverflow)?,\n );\n\n // 7. Emit an event to indicate a new user was added to the network\n Self::deposit_event(Event::Registered { id: sender });\n\n Ok(())\n }\n")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0aa99fa2.0f3e17a7.js b/assets/js/0aa99fa2.624d19e4.js similarity index 56% rename from assets/js/0aa99fa2.0f3e17a7.js rename to assets/js/0aa99fa2.624d19e4.js index 1ece3cdc8..9ff673bcd 100644 --- a/assets/js/0aa99fa2.0f3e17a7.js +++ b/assets/js/0aa99fa2.624d19e4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[9695],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},c="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),c=p(n),h=r,m=c["".concat(l,".").concat(h)]||c[h]||d[h]||i;return n?a.createElement(m,s(s({ref:t},u),{},{components:n})):a.createElement(m,s({ref:t},u))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,s=new Array(i);s[0]=h;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o[c]="string"==typeof e?e:r,s[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>d,frontMatter:()=>i,metadata:()=>o,toc:()=>p});var a=n(7462),r=(n(7294),n(3905));const i={id:"error-handling",title:"Error handling with Result & Option in Rust",sidebar_label:"Error handling with Result & Option",description:"Learn how to handle errors in Rust, and what the Error Type is."},s=void 0,o={unversionedId:"Rust/section4/error-handling",id:"Rust/section4/error-handling",title:"Error handling with Result & Option in Rust",description:"Learn how to handle errors in Rust, and what the Error Type is.",source:"@site/docs/Rust/section4/error-handling.md",sourceDirName:"Rust/section4",slug:"/Rust/section4/error-handling",permalink:"/docs/Rust/section4/error-handling",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Rust/section4/error-handling.md",tags:[],version:"current",lastUpdatedBy:"Bader Youssef",lastUpdatedAt:1679686936,formattedLastUpdatedAt:"Mar 24, 2023",frontMatter:{id:"error-handling",title:"Error handling with Result & Option in Rust",sidebar_label:"Error handling with Result & Option",description:"Learn how to handle errors in Rust, and what the Error Type is."},sidebar:"rust",previous:{title:"Panic! in Rust",permalink:"/docs/Rust/section4/panic"},next:{title:"Intro to Intermediate Rust - Data Structs & Collections",permalink:"/docs/Rust/section5/"}},l={},p=[{value:"Using Option",id:"using-option",level:2},{value:"Pattern Matching with Option",id:"pattern-matching-with-option",level:2},{value:"Using if let with Option",id:"using-if-let-with-option",level:2},{value:"Using Result",id:"using-result",level:2},{value:"Defining a Custom Error Type For Result",id:"defining-a-custom-error-type-for-result",level:2},{value:"Try it yourself!",id:"try-it-yourself",level:2},{value:"What's happening here?",id:"whats-happening-here",level:2}],u={toc:p},c="wrapper";function d(e){let{components:t,...n}=e;return(0,r.kt)(c,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"As seen before, ",(0,r.kt)("em",{parentName:"p"},"panicking")," when the program is running is unideal and should be avoided if necessary. Rust includes two commonly used ",(0,r.kt)("em",{parentName:"p"},"enums")," that help ensure data is valid in runtime: ",(0,r.kt)("inlineCode",{parentName:"p"},"Result")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"Option"),"."),(0,r.kt)("p",null,"Both types ensure invalid data and errors are handled adequately and do not cause the program to panic."),(0,r.kt)("h2",{id:"using-option"},"Using Option"),(0,r.kt)("p",null,"Option is an enum that contains two variants - ",(0,r.kt)("inlineCode",{parentName:"p"},"None")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"Some"),": "),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"enum Option {\n None,\n Some(T),\n}\n")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"T")," here may be a new sight to behold. This is a ",(0,r.kt)("strong",{parentName:"p"},"generic type parameter"),", which will be covered in module six. For now, know it means that ",(0,r.kt)("em",{parentName:"p"},"any")," type can be within ",(0,r.kt)("inlineCode",{parentName:"p"},"Some")," - it doesn't matter ",(0,r.kt)("em",{parentName:"p"},"what")," it is, just that something is there."),(0,r.kt)("p",null,"A concrete example would be attempting to access an empty array. Instead of panicking and attempting to access an index that doesn't exist, we can return an ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// An example of a function that returns an Option, aka, Some(i32) or None.\n// Notice the angled brackets which contain the type we're expecting.\nfn safe_access(index: usize, slice: &[i32]) -> Option {\n // We check to see if the length of the slice is zero, or\n // less than the requested index. If it is, we return `None`\n if slice.len() == 0 || slice.len() < index {\n return None;\n }\n // Otherwise, we're good to return the requested item!\n Some(slice[index])\n}\n")),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"None")," does the opposite. If the Option is ",(0,r.kt)("inlineCode",{parentName:"p"},"None"),", it implies the data does not exist. This is useful for checking the state of some data but does not describe any error associated with a potential failure."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"let empty_array = [];\nlet valid_array = [1, 2, 3];\n// Pass it in as a reference, as per the function signature\n\nsafe_access(1, &empty_array); // returns None\nsafe_access(1, &valid_array); // returns Some(2)\n")),(0,r.kt)("h2",{id:"pattern-matching-with-option"},"Pattern Matching with Option"),(0,r.kt)("p",null,"Because ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")," returns an enum, it would be good practice to ensure that we handle both variants. Functions can be called and matched directly:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'let empty_array = [];\nlet valid_array = [1, 2, 3];\n\n// Technically could be None or Some\n// hint: look at the type of this variable\nlet maybe_value: Option = safe_access(1, &valid_array);\n\n// However, let\'s match the function directly:\nmatch safe_access(1, &valid_array) {\n Some(value) => println!("We have a value: {value}"),\n None => println!("It doesn\'t exist :()")\n};\n\n')),(0,r.kt)("h2",{id:"using-if-let-with-option"},"Using ",(0,r.kt)("inlineCode",{parentName:"h2"},"if let")," with Option"),(0,r.kt)("p",null,"Alternatively, an ",(0,r.kt)("inlineCode",{parentName:"p"},"if let")," statement may be used instead of ",(0,r.kt)("inlineCode",{parentName:"p"},"match"),". ",(0,r.kt)("inlineCode",{parentName:"p"},"if let")," essentially will peform the same type of pattern matching, where it will look for ",(0,r.kt)("inlineCode",{parentName:"p"},"Some")," value, assign it to a variable if it exists, and safely ",(0,r.kt)("inlineCode",{parentName:"p"},"unwrap")," it:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'let valid_array = [1, 2, 3];\n\nif let Some(value) = safe_access(0, &valid_array) {\n println!("{value}"); // 1\n} else {\n println!("Nothing valid was found!");\n}\n')),(0,r.kt)("h2",{id:"using-result"},"Using Result"),(0,r.kt)("p",null,"A Result also contains two variants, ",(0,r.kt)("inlineCode",{parentName:"p"},"Ok(T)")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"Err(E)"),". While the generic ",(0,r.kt)("inlineCode",{parentName:"p"},"T")," still implies any value, the generic ",(0,r.kt)("inlineCode",{parentName:"p"},"E")," can be used to define a custom error type, allowing us to describe why a particular value or scenario did not output as expected."),(0,r.kt)("p",null,"It is used very similarly to ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")," at times, with the exception that instead of returning ",(0,r.kt)("inlineCode",{parentName:"p"},"None"),", it returns an ",(0,r.kt)("inlineCode",{parentName:"p"},"Error")," that describes what went wrong. Usually, ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")," should be used when a value is not certain to be existent or not, and ",(0,r.kt)("inlineCode",{parentName:"p"},"Result")," used when referring to a computation that has the possibility of failing."),(0,r.kt)("p",null,"You may read more on ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")," vs. ",(0,r.kt)("inlineCode",{parentName:"p"},"Result")," ",(0,r.kt)("a",{parentName:"p",href:"https://levelup.gitconnected.com/rust-option-vs-result-when-to-use-what-e73e82612cb0"},(0,r.kt)("strong",{parentName:"a"},"here")),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"enum Result {\n Ok(T),\n Err(E),\n}\n")),(0,r.kt)("p",null,"Here is the example above, only adapted to use a ",(0,r.kt)("inlineCode",{parentName:"p"},"Result"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'fn safe_access_result(index: usize, slice: &[i32]) -> Result {\n if slice.len() == 0 || slice.len() < index {\n return Err(String::from("Error! Invalid index!"));\n }\n Ok(slice[index])\n}\n')),(0,r.kt)("p",null,"A key aspect is the ",(0,r.kt)("inlineCode",{parentName:"p"},"E"),", or ",(0,r.kt)("inlineCode",{parentName:"p"},"Err"),", part of this expected return type. It's a String, meaning while it enables us to express why it has failed, it is unideal."),(0,r.kt)("h2",{id:"defining-a-custom-error-type-for-result"},"Defining a Custom Error Type For Result"),(0,r.kt)("p",null,"Rather than having obscure Strings as an Error type, we can instead define a custom enum to represent any potential errors:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"enum SafeArrayError {\n InvalidIndex\n}\n")),(0,r.kt)("p",null,"With this type defined, we can now change our function to the following:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"enum SafeArrayError {\n InvalidIndex\n}\n\nfn safe_access_result(index: usize, slice: &[i32]) -> Result {\n if slice.len() == 0 || slice.len() < index {\n // Now, we return a context-specific error.\n return Err(SafeArrayError::InvalidIndex);\n }\n Ok(slice[index])\n}\n")),(0,r.kt)("h2",{id:"try-it-yourself"},"Try it yourself!"),(0,r.kt)("iframe",{width:"100%",height:"580",src:"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&code=fn+main%28%29+%7B%0A++++let+empty_array+%3D+%5B%5D%3B%0A++++let+valid_array+%3D+%5B1%2C+2%2C+3%5D%3B%0A++++%2F%2F+Pass+it+in+as+a+reference%2C+as+per+the+function+signature%0A%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+safe_access%281%2C+%26empty_array%29%29%3B+%2F%2F+None%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+safe_access%281%2C+%26valid_array%29%29%3B+%2F%2F+Some%282%29%0A%0A++++%2F%2F+Using+match%0A++++match+safe_access%281%2C+%26valid_array%29+%7B%0A++++++++Some%28value%29+%3D%3E+println%21%28%22We+have+a+value%3A+%7Bvalue%7D%22%29%2C%0A++++++++None+%3D%3E+println%21%28%22It+doesn%27t+exist+%3A%28%29%22%29%2C%0A++++%7D%3B%0A%0A++++%2F%2F+Using+if+let%0A++++if+let+Some%28value%29+%3D+safe_access%280%2C+%26valid_array%29+%7B%0A++++++++println%21%28%22%7Bvalue%7D%22%29%3B+%2F%2F+1%0A++++%7D+else+%7B%0A++++++++println%21%28%22Nothing+valid+was+found%21%22%29%3B%0A++++%7D%0A%0A++++%2F%2F+Using+Result%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+safe_access_result%281%2C+%26empty_array%29%29%3B+%2F%2F+Err%28InvalidIndex%29%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+safe_access_result%281%2C+%26valid_array%29%29%3B+%2F%2F+Ok%282%29%0A%7D%0A%0A%2F%2F+An+example+of+a+function+that+returns+an+Option%2C+aka%2C+Some%28i32%29+or+None.%0A%2F%2F+Notice+the+angled+brackets+which+contain+the+type+we%27re+expecting.%0Afn+safe_access%28index%3A+usize%2C+slice%3A+%26%5Bi32%5D%29+-%3E+Option%3Ci32%3E+%7B%0A++++%2F%2F+We+check+to+see+if+the+length+of+the+slice+is+zero%2C+or%0A++++%2F%2F+less+than+the+requested+index.++If+it+is%2C+we+return+%60None%60%0A++++if+slice.len%28%29+%3D%3D+0+%7C%7C+slice.len%28%29+%3C+index+%7B%0A++++++++return+None%3B%0A++++%7D%0A++++%2F%2F+Otherwise%2C+we%27re+good+to+return+the+requested+item%21%0A++++Some%28slice%5Bindex%5D%29%0A%7D%0A%0A%23%5Bderive%28Debug%29%5D%0Aenum+SafeArrayError+%7B%0A++++InvalidIndex%2C%0A%7D%0A%0Afn+safe_access_result%28index%3A+usize%2C+slice%3A+%26%5Bi32%5D%29+-%3E+Result%3Ci32%2C+SafeArrayError%3E+%7B%0A++++if+slice.len%28%29+%3D%3D+0+%7C%7C+slice.len%28%29+%3C+index+%7B%0A++++++++%2F%2F+Now%2C+we+return+a+context-specific+error.%0A++++++++return+Err%28SafeArrayError%3A%3AInvalidIndex%29%3B%0A++++%7D%0A++++Ok%28slice%5Bindex%5D%29%0A%7D%0A"}),(0,r.kt)("h2",{id:"whats-happening-here"},"What's happening here?"),(0,r.kt)("p",null,"There are two functions, ",(0,r.kt)("inlineCode",{parentName:"p"},"safe_access")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"safe_access_result"),". These functions both accomplish the same task - ensure a particular element of an array can be accessed safely. A custom error type is also defined, ",(0,r.kt)("inlineCode",{parentName:"p"},"SafeArrayError"),", which is utilized within the ",(0,r.kt)("inlineCode",{parentName:"p"},"safe_access_result")," function."))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[9695],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},c="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),c=p(n),h=r,m=c["".concat(l,".").concat(h)]||c[h]||d[h]||i;return n?a.createElement(m,o(o({ref:t},u),{},{components:n})):a.createElement(m,o({ref:t},u))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[c]="string"==typeof e?e:r,o[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>p});var a=n(7462),r=(n(7294),n(3905));const i={id:"error-handling",title:"Error handling with Result & Option in Rust",sidebar_label:"Error handling with Result & Option",description:"Learn how to handle errors in Rust, and what the Error Type is."},o=void 0,s={unversionedId:"Rust/section4/error-handling",id:"Rust/section4/error-handling",title:"Error handling with Result & Option in Rust",description:"Learn how to handle errors in Rust, and what the Error Type is.",source:"@site/docs/Rust/section4/error-handling.md",sourceDirName:"Rust/section4",slug:"/Rust/section4/error-handling",permalink:"/docs/Rust/section4/error-handling",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Rust/section4/error-handling.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692960998,formattedLastUpdatedAt:"Aug 25, 2023",frontMatter:{id:"error-handling",title:"Error handling with Result & Option in Rust",sidebar_label:"Error handling with Result & Option",description:"Learn how to handle errors in Rust, and what the Error Type is."},sidebar:"rust",previous:{title:"Panic! in Rust",permalink:"/docs/Rust/section4/panic"},next:{title:"Intro to Intermediate Rust - Data Structs & Collections",permalink:"/docs/Rust/section5/"}},l={},p=[{value:"Using Option",id:"using-option",level:2},{value:"Pattern Matching with Option",id:"pattern-matching-with-option",level:2},{value:"Using if let with Option",id:"using-if-let-with-option",level:2},{value:"Using Result",id:"using-result",level:2},{value:"Defining a Custom Error Type For Result",id:"defining-a-custom-error-type-for-result",level:2},{value:"Try it yourself",id:"try-it-yourself",level:2},{value:"What's happening here?",id:"whats-happening-here",level:2}],u={toc:p},c="wrapper";function d(e){let{components:t,...n}=e;return(0,r.kt)(c,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"As seen before, ",(0,r.kt)("em",{parentName:"p"},"panicking")," when the program is running is unideal and should be avoided if necessary. Rust includes two commonly used ",(0,r.kt)("em",{parentName:"p"},"enums")," that help ensure data is valid in runtime: ",(0,r.kt)("inlineCode",{parentName:"p"},"Result")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"Option"),"."),(0,r.kt)("p",null,"Both types ensure invalid data and errors are handled adequately and do not cause the program to panic."),(0,r.kt)("h2",{id:"using-option"},"Using Option"),(0,r.kt)("p",null,"Option is an enum that contains two variants - ",(0,r.kt)("inlineCode",{parentName:"p"},"None")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"Some"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"enum Option {\n None,\n Some(T),\n}\n")),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"T")," here may be a new sight to behold. This is a ",(0,r.kt)("strong",{parentName:"p"},"generic type parameter"),", which will be covered in module six. For now, know it means that ",(0,r.kt)("em",{parentName:"p"},"any")," type can be within ",(0,r.kt)("inlineCode",{parentName:"p"},"Some")," - it doesn't matter ",(0,r.kt)("em",{parentName:"p"},"what")," it is, just that something is there."),(0,r.kt)("p",null,"A concrete example would be attempting to access an empty array. Instead of panicking and attempting to access an index that doesn't exist, we can return an ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"// An example of a function that returns an Option, aka, Some(i32) or None.\n// Notice the angled brackets which contain the type we're expecting.\nfn safe_access(index: usize, slice: &[i32]) -> Option {\n // We check to see if the length of the slice is zero, or\n // less than the requested index. If it is, we return `None`\n if slice.len() == 0 || slice.len() < index {\n return None;\n }\n // Otherwise, we're good to return the requested item!\n Some(slice[index])\n}\n")),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"None")," does the opposite. If the Option is ",(0,r.kt)("inlineCode",{parentName:"p"},"None"),", it implies the data does not exist. This is useful for checking the state of some data but does not describe any error associated with a potential failure."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"let empty_array = [];\nlet valid_array = [1, 2, 3];\n// Pass it in as a reference, as per the function signature\n\nsafe_access(1, &empty_array); // returns None\nsafe_access(1, &valid_array); // returns Some(2)\n")),(0,r.kt)("h2",{id:"pattern-matching-with-option"},"Pattern Matching with Option"),(0,r.kt)("p",null,"Because ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")," returns an enum, it would be good practice to ensure that we handle both variants. Functions can be called and matched directly:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'let empty_array = [];\nlet valid_array = [1, 2, 3];\n\n// Technically could be None or Some\n// hint: look at the type of this variable\nlet maybe_value: Option = safe_access(1, &valid_array);\n\n// However, let\'s match the function directly:\nmatch safe_access(1, &valid_array) {\n Some(value) => println!("We have a value: {value}"),\n None => println!("It doesn\'t exist :()")\n};\n\n')),(0,r.kt)("h2",{id:"using-if-let-with-option"},"Using ",(0,r.kt)("inlineCode",{parentName:"h2"},"if let")," with Option"),(0,r.kt)("p",null,"Alternatively, an ",(0,r.kt)("inlineCode",{parentName:"p"},"if let")," statement may be used instead of ",(0,r.kt)("inlineCode",{parentName:"p"},"match"),". ",(0,r.kt)("inlineCode",{parentName:"p"},"if let")," essentially will perform the same type of pattern matching, where it will look for ",(0,r.kt)("inlineCode",{parentName:"p"},"Some")," value, assign it to a variable if it exists, and safely ",(0,r.kt)("inlineCode",{parentName:"p"},"unwrap")," it:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'let valid_array = [1, 2, 3];\n\nif let Some(value) = safe_access(0, &valid_array) {\n println!("{value}"); // 1\n} else {\n println!("Nothing valid was found!");\n}\n')),(0,r.kt)("h2",{id:"using-result"},"Using Result"),(0,r.kt)("p",null,"A Result also contains two variants, ",(0,r.kt)("inlineCode",{parentName:"p"},"Ok(T)")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"Err(E)"),". While the generic ",(0,r.kt)("inlineCode",{parentName:"p"},"T")," still implies any value, the generic ",(0,r.kt)("inlineCode",{parentName:"p"},"E")," can be used to define a custom error type, allowing us to describe why a particular value or scenario did not output as expected."),(0,r.kt)("p",null,"It is used very similarly to ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")," at times, with the exception that instead of returning ",(0,r.kt)("inlineCode",{parentName:"p"},"None"),", it returns an ",(0,r.kt)("inlineCode",{parentName:"p"},"Error")," that describes what went wrong. Usually, ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")," should be used when a value is not certain to be existent or not, and ",(0,r.kt)("inlineCode",{parentName:"p"},"Result")," used when referring to a computation that has the possibility of failing."),(0,r.kt)("p",null,"You may read more on ",(0,r.kt)("inlineCode",{parentName:"p"},"Option")," vs. ",(0,r.kt)("inlineCode",{parentName:"p"},"Result")," ",(0,r.kt)("a",{parentName:"p",href:"https://levelup.gitconnected.com/rust-option-vs-result-when-to-use-what-e73e82612cb0"},(0,r.kt)("strong",{parentName:"a"},"here")),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"enum Result {\n Ok(T),\n Err(E),\n}\n")),(0,r.kt)("p",null,"Here is the example above, only adapted to use a ",(0,r.kt)("inlineCode",{parentName:"p"},"Result"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'fn safe_access_result(index: usize, slice: &[i32]) -> Result {\n if slice.len() == 0 || slice.len() < index {\n return Err(String::from("Error! Invalid index!"));\n }\n Ok(slice[index])\n}\n')),(0,r.kt)("p",null,"A key aspect is the ",(0,r.kt)("inlineCode",{parentName:"p"},"E"),", or ",(0,r.kt)("inlineCode",{parentName:"p"},"Err"),", part of this expected return type. It's a String, meaning while it enables us to express why it has failed, it is unideal."),(0,r.kt)("h2",{id:"defining-a-custom-error-type-for-result"},"Defining a Custom Error Type For Result"),(0,r.kt)("p",null,"Rather than having obscure Strings as an Error type, we can instead define a custom enum to represent any potential errors:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"enum SafeArrayError {\n InvalidIndex\n}\n")),(0,r.kt)("p",null,"With this type defined, we can now change our function to the following:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"enum SafeArrayError {\n InvalidIndex\n}\n\nfn safe_access_result(index: usize, slice: &[i32]) -> Result {\n if slice.len() == 0 || slice.len() < index {\n // Now, we return a context-specific error.\n return Err(SafeArrayError::InvalidIndex);\n }\n Ok(slice[index])\n}\n")),(0,r.kt)("h2",{id:"try-it-yourself"},"Try it yourself"),(0,r.kt)("iframe",{width:"100%",height:"580",src:"https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&code=fn+main%28%29+%7B%0A++++let+empty_array+%3D+%5B%5D%3B%0A++++let+valid_array+%3D+%5B1%2C+2%2C+3%5D%3B%0A++++%2F%2F+Pass+it+in+as+a+reference%2C+as+per+the+function+signature%0A%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+safe_access%281%2C+%26empty_array%29%29%3B+%2F%2F+None%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+safe_access%281%2C+%26valid_array%29%29%3B+%2F%2F+Some%282%29%0A%0A++++%2F%2F+Using+match%0A++++match+safe_access%281%2C+%26valid_array%29+%7B%0A++++++++Some%28value%29+%3D%3E+println%21%28%22We+have+a+value%3A+%7Bvalue%7D%22%29%2C%0A++++++++None+%3D%3E+println%21%28%22It+doesn%27t+exist+%3A%28%29%22%29%2C%0A++++%7D%3B%0A%0A++++%2F%2F+Using+if+let%0A++++if+let+Some%28value%29+%3D+safe_access%280%2C+%26valid_array%29+%7B%0A++++++++println%21%28%22%7Bvalue%7D%22%29%3B+%2F%2F+1%0A++++%7D+else+%7B%0A++++++++println%21%28%22Nothing+valid+was+found%21%22%29%3B%0A++++%7D%0A%0A++++%2F%2F+Using+Result%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+safe_access_result%281%2C+%26empty_array%29%29%3B+%2F%2F+Err%28InvalidIndex%29%0A++++println%21%28%22%7B%3A%3F%7D%22%2C+safe_access_result%281%2C+%26valid_array%29%29%3B+%2F%2F+Ok%282%29%0A%7D%0A%0A%2F%2F+An+example+of+a+function+that+returns+an+Option%2C+aka%2C+Some%28i32%29+or+None.%0A%2F%2F+Notice+the+angled+brackets+which+contain+the+type+we%27re+expecting.%0Afn+safe_access%28index%3A+usize%2C+slice%3A+%26%5Bi32%5D%29+-%3E+Option%3Ci32%3E+%7B%0A++++%2F%2F+We+check+to+see+if+the+length+of+the+slice+is+zero%2C+or%0A++++%2F%2F+less+than+the+requested+index.++If+it+is%2C+we+return+%60None%60%0A++++if+slice.len%28%29+%3D%3D+0+%7C%7C+slice.len%28%29+%3C+index+%7B%0A++++++++return+None%3B%0A++++%7D%0A++++%2F%2F+Otherwise%2C+we%27re+good+to+return+the+requested+item%21%0A++++Some%28slice%5Bindex%5D%29%0A%7D%0A%0A%23%5Bderive%28Debug%29%5D%0Aenum+SafeArrayError+%7B%0A++++InvalidIndex%2C%0A%7D%0A%0Afn+safe_access_result%28index%3A+usize%2C+slice%3A+%26%5Bi32%5D%29+-%3E+Result%3Ci32%2C+SafeArrayError%3E+%7B%0A++++if+slice.len%28%29+%3D%3D+0+%7C%7C+slice.len%28%29+%3C+index+%7B%0A++++++++%2F%2F+Now%2C+we+return+a+context-specific+error.%0A++++++++return+Err%28SafeArrayError%3A%3AInvalidIndex%29%3B%0A++++%7D%0A++++Ok%28slice%5Bindex%5D%29%0A%7D%0A"}),(0,r.kt)("h2",{id:"whats-happening-here"},"What's happening here?"),(0,r.kt)("p",null,"There are two functions, ",(0,r.kt)("inlineCode",{parentName:"p"},"safe_access")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"safe_access_result"),". These functions both accomplish the same task - ensure a particular element of an array can be accessed safely. A custom error type is also defined, ",(0,r.kt)("inlineCode",{parentName:"p"},"SafeArrayError"),", which is utilized within the ",(0,r.kt)("inlineCode",{parentName:"p"},"safe_access_result")," function."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/14263310.68ffe623.js b/assets/js/14263310.68ffe623.js deleted file mode 100644 index 3614ff189..000000000 --- a/assets/js/14263310.68ffe623.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[3369],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>y});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),h=c(n),p=r,y=h["".concat(l,".").concat(p)]||h[p]||u[p]||o;return n?a.createElement(y,i(i({ref:t},d),{},{components:n})):a.createElement(y,i({ref:t},d))}));function y(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=p;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[h]="string"==typeof e?e:r,i[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var a=n(7462),r=(n(7294),n(3905));const o={id:"interoperability"},i="Interoperability",s={unversionedId:"Polkadot/Module5/interoperability",id:"Polkadot/Module5/interoperability",title:"Interoperability",description:"XCMP",source:"@site/docs/Polkadot/Module5/interoperability.md",sourceDirName:"Polkadot/Module5",slug:"/Polkadot/Module5/interoperability",permalink:"/docs/Polkadot/Module5/interoperability",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module5/interoperability.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1654881714,formattedLastUpdatedAt:"Jun 10, 2022",frontMatter:{id:"interoperability"},sidebar:"polkadot",previous:{title:"Decentralization of Network",permalink:"/docs/Polkadot/Module4/decentralization"},next:{title:"Polkadot Bridges",permalink:"/docs/Polkadot/Module5/bridges"}},l={},c=[{value:"XCMP",id:"xcmp",level:2}],d={toc:c},h="wrapper";function u(e){let{components:t,...n}=e;return(0,r.kt)(h,(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"interoperability"},"Interoperability"),(0,r.kt)("h2",{id:"xcmp"},"XCMP"),(0,r.kt)("p",null,"If an account on one parachain sends tokens to another parachain, then XCMP\nensures that this message is delivered correctly. It is sent at a speed which is not dependent on how\nlong it takes to finalise blocks, which means that it needs to deal with the possibility of Polkadot\nforking. Thus we optimistically execute based on the assumption that the parachain blocks are\ncorrect. If one is not, then we need to revert and for that, it is important that parachains only\nreceive messages that were sent by blocks recorded on this new relay chain fork, and not the\nreverted fork. Thus we need that the parachain and XCMP logic ensure that a fork of the relay\nchain defines a consistent history of Polkadot and thus messages only arrive when they have been\nsent previously in the history de\u2000ned by this fork."),(0,r.kt)("p",null,"XCMP is the protocol that parachains use to send messages to each other. It aims to guarantee\nfour things: \u2000rst that messages arrive quickly; second that messages from one parachain arrive to\nanother in order; third that arriving messages were indeed sent in the finalised history of the sending\nchain; and fourth that recipients will receive messages fairly across senders, helping guarantee that\nsenders never wait inde\u2000nitely for their messages to be seen.\nThere are two parts to XCMP. (1) Metadata about outgoing messages for a parachain block\nare included on the relay chain and later this metadata is used to authenticate messages by the\nreceiving parachain. (2) The message bodies corresponding to this metadata need to be actually\ndistributed from the senders to the recipients, together with a proof that the message body is actu-\nally associated with the relevant metadata. The details of distribution are covered as a networking\nprotocol in Cross-chain message; the remainder is covered below.\nThe way relay chain blocks include headers of parachain blocks gives a synchronous notion\nof time for parachain blocks, just by relay chain block numbers. Additionally it allows us to\nauthenticate messages as being sent in the history given by the relay chain i.e. it is impossible\nthat one parachain sends a message, then reorgs 2 so that that message was not sent, but has\nbeen received. This holds even though the system may not have reached \u2000nality over whether the\nmessage was sent, because any relay chain provides a consistent history.\nBecause we require parachains to act on every message eventually, non-delivery of a single\nmessage can potentially stop a parachain from being able to build blocks. Consequently we need\nenough redundancy in our message delivery system. Any validators who validate the PoV block\nshould keep any outgoing messages from that block available for a day or so and all full nodes of\nthe sending parachain also store the outgoing messages until they know they have been acted on."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/14263310.b1464a13.js b/assets/js/14263310.b1464a13.js new file mode 100644 index 000000000..d4fbb7269 --- /dev/null +++ b/assets/js/14263310.b1464a13.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[3369],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>y});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),h=c(n),p=r,y=h["".concat(l,".").concat(p)]||h[p]||u[p]||o;return n?a.createElement(y,i(i({ref:t},d),{},{components:n})):a.createElement(y,i({ref:t},d))}));function y(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=p;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[h]="string"==typeof e?e:r,i[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var a=n(7462),r=(n(7294),n(3905));const o={id:"interoperability"},i="Interoperability",s={unversionedId:"Polkadot/Module5/interoperability",id:"Polkadot/Module5/interoperability",title:"Interoperability",description:"XCMP",source:"@site/docs/Polkadot/Module5/interoperability.md",sourceDirName:"Polkadot/Module5",slug:"/Polkadot/Module5/interoperability",permalink:"/docs/Polkadot/Module5/interoperability",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module5/interoperability.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"interoperability"},sidebar:"polkadot",previous:{title:"Decentralization of Network",permalink:"/docs/Polkadot/Module4/decentralization"},next:{title:"Polkadot Bridges",permalink:"/docs/Polkadot/Module5/bridges"}},l={},c=[{value:"XCMP",id:"xcmp",level:2}],d={toc:c},h="wrapper";function u(e){let{components:t,...n}=e;return(0,r.kt)(h,(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"interoperability"},"Interoperability"),(0,r.kt)("h2",{id:"xcmp"},"XCMP"),(0,r.kt)("p",null,"If an account on one parachain sends tokens to another parachain, then XCMP\nensures that this message is delivered correctly. It is sent at a speed which is not dependent on how\nlong it takes to finalise blocks, which means that it needs to deal with the possibility of Polkadot\nforking. Thus we optimistically execute based on the assumption that the parachain blocks are\ncorrect. If one is not, then we need to revert and for that, it is important that parachains only\nreceive messages that were sent by blocks recorded on this new relay chain fork, and not the\nreverted fork. Thus we need that the parachain and XCMP logic ensure that a fork of the relay\nchain defines a consistent history of Polkadot and thus messages only arrive when they have been\nsent previously in the history defined by this fork."),(0,r.kt)("p",null,"XCMP is the protocol that parachains use to send messages to each other. It aims to guarantee\nfour things: first that messages arrive quickly; second that messages from one parachain arrive to\nanother in order; third that arriving messages were indeed sent in the finalised history of the sending\nchain; and fourth that recipients will receive messages fairly across senders, helping guarantee that\nsenders never wait indefinitely for their messages to be seen.\nThere are two parts to XCMP. (1) Metadata about outgoing messages for a parachain block\nare included on the relay chain and later this metadata is used to authenticate messages by the\nreceiving parachain. (2) The message bodies corresponding to this metadata need to be actually\ndistributed from the senders to the recipients, together with a proof that the message body is actu-\nally associated with the relevant metadata. The details of distribution are covered as a networking\nprotocol in Cross-chain message; the remainder is covered below.\nThe way relay chain blocks include headers of parachain blocks gives a synchronous notion\nof time for parachain blocks, just by relay chain block numbers. Additionally it allows us to\nauthenticate messages as being sent in the history given by the relay chain i.e. it is impossible\nthat one parachain sends a message, then reorgs 2 so that that message was not sent, but has\nbeen received. This holds even though the system may not have reached finality over whether the\nmessage was sent, because any relay chain provides a consistent history.\nBecause we require parachains to act on every message eventually, non-delivery of a single\nmessage can potentially stop a parachain from being able to build blocks. Consequently we need\nenough redundancy in our message delivery system. Any validators who validate the PoV block\nshould keep any outgoing messages from that block available for a day or so and all full nodes of\nthe sending parachain also store the outgoing messages until they know they have been acted on."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/47521d56.6e991101.js b/assets/js/47521d56.6e991101.js deleted file mode 100644 index f08ece6b7..000000000 --- a/assets/js/47521d56.6e991101.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[901],{3905:(e,t,o)=>{o.d(t,{Zo:()=>d,kt:()=>m});var n=o(7294);function a(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function r(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function i(e){for(var t=1;t=0||(a[o]=e[o]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(a[o]=e[o])}return a}var c=n.createContext({}),s=function(e){var t=n.useContext(c),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},d=function(e){var t=s(e.components);return n.createElement(c.Provider,{value:t},e.children)},u="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var o=e.components,a=e.mdxType,r=e.originalType,c=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=s(o),p=a,m=u["".concat(c,".").concat(p)]||u[p]||h[p]||r;return o?n.createElement(m,i(i({ref:t},d),{},{components:o})):n.createElement(m,i({ref:t},d))}));function m(e,t){var o=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=o.length,i=new Array(r);i[0]=p;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var s=2;s{o.r(t),o.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>h,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var n=o(7462),a=(o(7294),o(3905));const r={id:"governance"},i="On-Chain Governance",l={unversionedId:"Polkadot/Module2/governance",id:"Polkadot/Module2/governance",title:"On-Chain Governance",description:"The relay chain's logic itself will need updating occasionally. The governance mechanism",source:"@site/docs/Polkadot/Module2/governance.md",sourceDirName:"Polkadot/Module2",slug:"/Polkadot/Module2/governance",permalink:"/docs/Polkadot/Module2/governance",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module2/governance.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1656503095,formattedLastUpdatedAt:"Jun 29, 2022",frontMatter:{id:"governance"},sidebar:"polkadot",previous:{title:"Exploring Polkadot Network",permalink:"/docs/Polkadot/Module2/explorenetwork"},next:{title:"Treasury",permalink:"/docs/Polkadot/Module2/treasury"}},c={},s=[{value:"Forkless Upgrades through On-chain Governance",id:"forkless-upgrades-through-on-chain-governance",level:2},{value:"Self-Sustaining Blockchain",id:"self-sustaining-blockchain",level:2},{value:"Polkadot Council",id:"polkadot-council",level:2},{value:"Turn out Biasing",id:"turn-out-biasing",level:2}],d={toc:s},u="wrapper";function h(e){let{components:t,...o}=e;return(0,a.kt)(u,(0,n.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"on-chain-governance"},"On-Chain Governance"),(0,a.kt)("p",null,"The relay chain's logic itself will need updating occasionally. The governance mechanism\nallows Polkadot token holders to participate in the decision-making process rather than\nhaving any changes to the system be imposed by a central authority { or in the case of some de-\ncentralised systems, by a team of developers. Too often, a contentious code change has led existing\nblockchains to an impasse or a permanent fork. We want a mechanism that balances being able\nto make uncontentious changes quickly when needed, while also providing the tools to deal with\ncontentious proposals in a decisive and fair manner. The ultimate arbiters of Polkadot are the DOT\ntoken holders and so all important decisions, such as code changes, are made by state-weighted referenda.\nThere is an elected council, responsible for making smaller decisions and partially setting\nthe priority for referenda, in such a way that they cannot block a change that a majority wants."),(0,a.kt)("p",null,"Polkadot uses sophisticated mechanisms for Governance which allows it to evolve gracefully over\ntime at the ultimate behest of its assembled stakeholders. A key and unfailing rule is that all\nchanges to the protocol must be agreed upon by stake-weighted referendum { the majority of\nstake always commands the network.\nIn order to make any changes to the network, the idea is to bring DOT holders together and\nadministrate a network upgrade decision with the help of the Council (see Section 4.6.2). No\nmatter whether the proposal is submitted by a DOT holder or by the Council, it will ultimately\nhave to go through a referendum to let all DOT holders, weighted by stake, make the decision.\nEach DOT holder in Polkadot has the right to: a) submit a proposal, b) endorse a public\nproposal to prioritise it in the referendum timetable, c) vote on all active referenda, d) become a\ncandidate for a seat in the Council, and e) vote on candidates for the Council. In addition, any\nDOT holder may become a nominator or a validator candidate to participate in NPoS"),(0,a.kt)("h2",{id:"forkless-upgrades-through-on-chain-governance"},"Forkless Upgrades through On-chain Governance"),(0,a.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/3pAyvkgGLsU",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,a.kt)("h2",{id:"self-sustaining-blockchain"},"Self-Sustaining Blockchain"),(0,a.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/o--e-6J7C6o",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,a.kt)("h2",{id:"polkadot-council"},"Polkadot Council"),(0,a.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/qpv0Bm_KyFY",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,a.kt)("h2",{id:"turn-out-biasing"},"Turn out Biasing"),(0,a.kt)("p",null,"Turnout biasing: It may seem restrictive to force a full stakeholder-based process to do some-\nthing as little as, say, nudging the block time down by 5%. However, without this rule the network\nwould likely be unstable, as placing its control outside of the hands of stakeholders would create a\nmisalignment that may lead to inaction or worse. However, by taking advantage of the fact that\nturnout is rarely 100%, we can e\u2000ect di\u2000erent outcomes depending on the circumstances, crafting\na balance of power between active and passive stakeholders. For example, simple voting systems\ntypically introduce a notion of quorum, whereby a minimum amount of turnout must be reached\nbefore a change is passed."),(0,a.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/8CwYtcS5v18",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/47521d56.ce090131.js b/assets/js/47521d56.ce090131.js new file mode 100644 index 000000000..db3d8f7e4 --- /dev/null +++ b/assets/js/47521d56.ce090131.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[901],{3905:(e,t,o)=>{o.d(t,{Zo:()=>d,kt:()=>m});var n=o(7294);function a(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function r(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function i(e){for(var t=1;t=0||(a[o]=e[o]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(a[o]=e[o])}return a}var c=n.createContext({}),s=function(e){var t=n.useContext(c),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},d=function(e){var t=s(e.components);return n.createElement(c.Provider,{value:t},e.children)},u="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var o=e.components,a=e.mdxType,r=e.originalType,c=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),u=s(o),p=a,m=u["".concat(c,".").concat(p)]||u[p]||h[p]||r;return o?n.createElement(m,i(i({ref:t},d),{},{components:o})):n.createElement(m,i({ref:t},d))}));function m(e,t){var o=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=o.length,i=new Array(r);i[0]=p;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var s=2;s{o.r(t),o.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>h,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var n=o(7462),a=(o(7294),o(3905));const r={id:"governance"},i="On-Chain Governance",l={unversionedId:"Polkadot/Module2/governance",id:"Polkadot/Module2/governance",title:"On-Chain Governance",description:"The relay chain's logic itself will need updating occasionally. The governance mechanism",source:"@site/docs/Polkadot/Module2/governance.md",sourceDirName:"Polkadot/Module2",slug:"/Polkadot/Module2/governance",permalink:"/docs/Polkadot/Module2/governance",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module2/governance.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"governance"},sidebar:"polkadot",previous:{title:"Exploring Polkadot Network",permalink:"/docs/Polkadot/Module2/explorenetwork"},next:{title:"Treasury",permalink:"/docs/Polkadot/Module2/treasury"}},c={},s=[{value:"Forkless Upgrades through On-chain Governance",id:"forkless-upgrades-through-on-chain-governance",level:2},{value:"Self-Sustaining Blockchain",id:"self-sustaining-blockchain",level:2},{value:"Polkadot Council",id:"polkadot-council",level:2},{value:"Turn out Biasing",id:"turn-out-biasing",level:2}],d={toc:s},u="wrapper";function h(e){let{components:t,...o}=e;return(0,a.kt)(u,(0,n.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"on-chain-governance"},"On-Chain Governance"),(0,a.kt)("p",null,"The relay chain's logic itself will need updating occasionally. The governance mechanism\nallows Polkadot token holders to participate in the decision-making process rather than\nhaving any changes to the system be imposed by a central authority { or in the case of some de-\ncentralised systems, by a team of developers. Too often, a contentious code change has led existing\nblockchains to an impasse or a permanent fork. We want a mechanism that balances being able\nto make uncontentious changes quickly when needed, while also providing the tools to deal with\ncontentious proposals in a decisive and fair manner. The ultimate arbiters of Polkadot are the DOT\ntoken holders and so all important decisions, such as code changes, are made by state-weighted referenda.\nThere is an elected council, responsible for making smaller decisions and partially setting\nthe priority for referenda, in such a way that they cannot block a change that a majority wants."),(0,a.kt)("p",null,"Polkadot uses sophisticated mechanisms for Governance which allows it to evolve gracefully over\ntime at the ultimate behest of its assembled stakeholders. A key and unfailing rule is that all\nchanges to the protocol must be agreed upon by stake-weighted referendum { the majority of\nstake always commands the network.\nIn order to make any changes to the network, the idea is to bring DOT holders together and\nadministrate a network upgrade decision with the help of the Council (see Section 4.6.2). No\nmatter whether the proposal is submitted by a DOT holder or by the Council, it will ultimately\nhave to go through a referendum to let all DOT holders, weighted by stake, make the decision.\nEach DOT holder in Polkadot has the right to: a) submit a proposal, b) endorse a public\nproposal to prioritise it in the referendum timetable, c) vote on all active referenda, d) become a\ncandidate for a seat in the Council, and e) vote on candidates for the Council. In addition, any\nDOT holder may become a nominator or a validator candidate to participate in NPoS"),(0,a.kt)("h2",{id:"forkless-upgrades-through-on-chain-governance"},"Forkless Upgrades through On-chain Governance"),(0,a.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/3pAyvkgGLsU",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,a.kt)("h2",{id:"self-sustaining-blockchain"},"Self-Sustaining Blockchain"),(0,a.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/o--e-6J7C6o",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,a.kt)("h2",{id:"polkadot-council"},"Polkadot Council"),(0,a.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/qpv0Bm_KyFY",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,a.kt)("h2",{id:"turn-out-biasing"},"Turn out Biasing"),(0,a.kt)("p",null,"Turnout biasing: It may seem restrictive to force a full stakeholder-based process to do some-\nthing as little as, say, nudging the block time down by 5%. However, without this rule the network\nwould likely be unstable, as placing its control outside of the hands of stakeholders would create a\nmisalignment that may lead to inaction or worse. However, by taking advantage of the fact that\nturnout is rarely 100%, we can effect different outcomes depending on the circumstances, crafting\na balance of power between active and passive stakeholders. For example, simple voting systems\ntypically introduce a notion of quorum, whereby a minimum amount of turnout must be reached\nbefore a change is passed."),(0,a.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/8CwYtcS5v18",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4a418313.d6dba999.js b/assets/js/4a418313.d6dba999.js deleted file mode 100644 index 2036fc383..000000000 --- a/assets/js/4a418313.d6dba999.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[6175],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>f});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,c=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),h=l(n),p=o,f=h["".concat(c,".").concat(p)]||h[p]||u[p]||r;return n?a.createElement(f,i(i({ref:t},d),{},{components:n})):a.createElement(f,i({ref:t},d))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=p;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[h]="string"==typeof e?e:o,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var a=n(7462),o=(n(7294),n(3905));const r={id:"explorenetwork"},i="Exploring Polkadot Network",s={unversionedId:"Polkadot/Module2/explorenetwork",id:"Polkadot/Module2/explorenetwork",title:"Exploring Polkadot Network",description:"Polkadot as a State Machine",source:"@site/docs/Polkadot/Module2/explorenetwork.md",sourceDirName:"Polkadot/Module2",slug:"/Polkadot/Module2/explorenetwork",permalink:"/docs/Polkadot/Module2/explorenetwork",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module2/explorenetwork.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1656502743,formattedLastUpdatedAt:"Jun 29, 2022",frontMatter:{id:"explorenetwork"},sidebar:"polkadot",previous:{title:"DOT Token Utility",permalink:"/docs/Polkadot/Module2/dotutility"},next:{title:"On-Chain Governance",permalink:"/docs/Polkadot/Module2/governance"}},c={},l=[{value:"Polkadot as a State Machine",id:"polkadot-as-a-state-machine",level:2},{value:"State",id:"state",level:2},{value:"State Transitions",id:"state-transitions",level:2},{value:"Extrinsics",id:"extrinsics",level:2},{value:"Transaction Fees",id:"transaction-fees",level:2}],d={toc:l},h="wrapper";function u(e){let{components:t,...n}=e;return(0,o.kt)(h,(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"exploring-polkadot-network"},"Exploring Polkadot Network"),(0,o.kt)("h2",{id:"polkadot-as-a-state-machine"},"Polkadot as a State Machine"),(0,o.kt)("p",null,"Polkadot is a replicated sharded state machine where shards are the parachains and the\nPolkadot relay chain is part of the protocol ensuring global consensus among all the parachains.\nTherefore, the Polkadot relay chain protocol, can itself be considered as a replicated state machine\non its own. In this sense, this section describes the relay chain protocol by specifying the state\nmachine governing the relay chain. To that end, we describe the relay chain state and the detail\nof state transition governed by transactions grouped in the relay chain blocks."),(0,o.kt)("h2",{id:"state"},"State"),(0,o.kt)("p",null,"The state is represented through the use of an associative array data structure composed\nby a collection of (key; value) pairs where each key is unique. There is no assumption on the\nformat of the key or the value stored under it besides the fact that they both the key and the value\nneed to be \u2000nite byte arrays.\nThe (key; value) pairs which comprise the relay chain state are arranged in a Merkle radix-16\ntree. The root of this tree canonically identi\u2000es the current state of the relay chain. The Merkle\ntree also provides an e\u2000cient mean to produce the proof of inclusion for an individual pair in the\nstate.\nTo keep the state size in control, the relay chain state is solely used to facilitate the relay chain\noperations such as staking and identifying validators. The Merkle Radix tree is not supposed to\nstore any information regarding the internal operation of the parachains."),(0,o.kt)("h2",{id:"state-transitions"},"State Transitions"),(0,o.kt)("p",null,'Like any transaction-based transition system, Polkadot state changes via an\nexecuting ordered set of instructions, known as extrinsics. These extrinsics include transactions\nsubmitted by the public. They cover any data provided from \\outside" of the machine\'s state which\ncan a\u2000ect state transition. Polkadot relay chain is divided into two major components, namely the\n\\Runtime" and the \\Runtime environment". The execution logic of the state-transition function\nis mainly encapsulated in the Runtime while all other generic operations, commonly shared among\nmodern blockchain-based replicated state machines, are embedded into the Runtime environment.\nIn particular, the latter is in charge of network communication, block production and consensus\nengines. Runtime functions are compiled into a WebAssembly module and are stored as part of the\nstate. The Runtime environment communicates the extrinsics to the Runtime and interacts with\nit to execute the state transition. In this way, the state transition logic itself can be upgraded as\na part of the state transition.'),(0,o.kt)("h2",{id:"extrinsics"},"Extrinsics"),(0,o.kt)("p",null,'Extrinsics are the input data supplied to the Polkadot relay-chain state machine to\ntransition to new states. Extrinsics need to be stored into blocks of the relay chain in order to\nachieve consensus among the state machine replica. Extrinsics are divided into two broad categories\nnamely: transactions and "inherents" which represent data that is inherent to a relay chain block.\nThe timestamp t of a block is an example of inherent extrinsics which must be included in each\nPolkadot relay chain block.'),(0,o.kt)("p",null,"Transactions are signed and are gossiped around on the network between nodes. In contrast,\ninherents are not signed and are not gossiped individually but rather only when they are included\nin a block. The inherents in a block are assumed to be valid if a supermajority of validators\nassumes so. Transactions on the relay chain are mainly concerned with the operation of the relay\nchain and Polkadot protocol as a whole, such as set code, transfer, bond, validate, nominate,\nvote."),(0,o.kt)("p",null,"Relay chain block producers listen to all transaction network messages. Upon receiving a\ntransaction message, the transaction(s) are validated by the Runtime. The valid transactions then\nare arranged in a queue based on their priority and dependency and are considered for inclusion\nin future blocks accordingly."),(0,o.kt)("h2",{id:"transaction-fees"},"Transaction Fees"),(0,o.kt)("p",null,"We use the model described above to set the fee level of a transaction based\non three parameters: its type, its on-chain length, and its expected resource usage. This fee\ndifferentiation is used to reflect the different costs that a transaction incurs on the network and\non the state, and to encourage the processing of certain types of transactions over others. A\nfraction of every transaction fee is paid to the block producer, while another fraction goes to\n\u2000nance the Treasury. We highlight that, for a block producer, the rewards coming\nfrom transaction fees may constitute only a small fraction of their overall revenue, just enough to\nincentivise inclusion on the block.\nWe also run an adaptive transaction fee schedule that reacts to the traffic level, and ensures\nthat blocks are typically far from full, so that peaks of activity can be dealt with e\u2000ectively and\nlong inclusion times are rare. In particular, the fee of each transaction is multiplied by a parameter\nthat evolves over time depending on the current network traffic.\nWe make fees evolve slowly enough, so that the fee of any transaction can be predicted accu-\nrately within a frame of an hour. In particular, we do not intend for transaction fees to be the\nmain source of income for stakers."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4a418313.dfac47c1.js b/assets/js/4a418313.dfac47c1.js new file mode 100644 index 000000000..0a0422a62 --- /dev/null +++ b/assets/js/4a418313.dfac47c1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[6175],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>f});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,c=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),h=l(n),p=o,f=h["".concat(c,".").concat(p)]||h[p]||u[p]||r;return n?a.createElement(f,i(i({ref:t},d),{},{components:n})):a.createElement(f,i({ref:t},d))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=p;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[h]="string"==typeof e?e:o,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var a=n(7462),o=(n(7294),n(3905));const r={id:"explorenetwork"},i="Exploring Polkadot Network",s={unversionedId:"Polkadot/Module2/explorenetwork",id:"Polkadot/Module2/explorenetwork",title:"Exploring Polkadot Network",description:"Polkadot as a State Machine",source:"@site/docs/Polkadot/Module2/explorenetwork.md",sourceDirName:"Polkadot/Module2",slug:"/Polkadot/Module2/explorenetwork",permalink:"/docs/Polkadot/Module2/explorenetwork",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module2/explorenetwork.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"explorenetwork"},sidebar:"polkadot",previous:{title:"DOT Token Utility",permalink:"/docs/Polkadot/Module2/dotutility"},next:{title:"On-Chain Governance",permalink:"/docs/Polkadot/Module2/governance"}},c={},l=[{value:"Polkadot as a State Machine",id:"polkadot-as-a-state-machine",level:2},{value:"State",id:"state",level:2},{value:"State Transitions",id:"state-transitions",level:2},{value:"Extrinsics",id:"extrinsics",level:2},{value:"Transaction Fees",id:"transaction-fees",level:2}],d={toc:l},h="wrapper";function u(e){let{components:t,...n}=e;return(0,o.kt)(h,(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"exploring-polkadot-network"},"Exploring Polkadot Network"),(0,o.kt)("h2",{id:"polkadot-as-a-state-machine"},"Polkadot as a State Machine"),(0,o.kt)("p",null,"Polkadot is a replicated sharded state machine where shards are the parachains and the\nPolkadot relay chain is part of the protocol ensuring global consensus among all the parachains.\nTherefore, the Polkadot relay chain protocol, can itself be considered as a replicated state machine\non its own. In this sense, this section describes the relay chain protocol by specifying the state\nmachine governing the relay chain. To that end, we describe the relay chain state and the detail\nof state transition governed by transactions grouped in the relay chain blocks."),(0,o.kt)("h2",{id:"state"},"State"),(0,o.kt)("p",null,"The state is represented through the use of an associative array data structure composed\nby a collection of (key; value) pairs where each key is unique. There is no assumption on the\nformat of the key or the value stored under it besides the fact that they both the key and the value\nneed to be finite byte arrays.\nThe (key; value) pairs which comprise the relay chain state are arranged in a Merkle radix-16\ntree. The root of this tree canonically identifies the current state of the relay chain. The Merkle\ntree also provides an efficient mean to produce the proof of inclusion for an individual pair in the\nstate.\nTo keep the state size in control, the relay chain state is solely used to facilitate the relay chain\noperations such as staking and identifying validators. The Merkle Radix tree is not supposed to\nstore any information regarding the internal operation of the parachains."),(0,o.kt)("h2",{id:"state-transitions"},"State Transitions"),(0,o.kt)("p",null,'Like any transaction-based transition system, Polkadot state changes via an\nexecuting ordered set of instructions, known as extrinsics. These extrinsics include transactions\nsubmitted by the public. They cover any data provided from \\outside" of the machine\'s state which\ncan affect state transition. Polkadot relay chain is divided into two major components, namely the\n\\Runtime" and the \\Runtime environment". The execution logic of the state-transition function\nis mainly encapsulated in the Runtime while all other generic operations, commonly shared among\nmodern blockchain-based replicated state machines, are embedded into the Runtime environment.\nIn particular, the latter is in charge of network communication, block production and consensus\nengines. Runtime functions are compiled into a WebAssembly module and are stored as part of the\nstate. The Runtime environment communicates the extrinsics to the Runtime and interacts with\nit to execute the state transition. In this way, the state transition logic itself can be upgraded as\na part of the state transition.'),(0,o.kt)("h2",{id:"extrinsics"},"Extrinsics"),(0,o.kt)("p",null,'Extrinsics are the input data supplied to the Polkadot relay-chain state machine to\ntransition to new states. Extrinsics need to be stored into blocks of the relay chain in order to\nachieve consensus among the state machine replica. Extrinsics are divided into two broad categories\nnamely: transactions and "inherents" which represent data that is inherent to a relay chain block.\nThe timestamp t of a block is an example of inherent extrinsics which must be included in each\nPolkadot relay chain block.'),(0,o.kt)("p",null,"Transactions are signed and are gossiped around on the network between nodes. In contrast,\ninherents are not signed and are not gossiped individually but rather only when they are included\nin a block. The inherents in a block are assumed to be valid if a supermajority of validators\nassumes so. Transactions on the relay chain are mainly concerned with the operation of the relay\nchain and Polkadot protocol as a whole, such as set code, transfer, bond, validate, nominate,\nvote."),(0,o.kt)("p",null,"Relay chain block producers listen to all transaction network messages. Upon receiving a\ntransaction message, the transaction(s) are validated by the Runtime. The valid transactions then\nare arranged in a queue based on their priority and dependency and are considered for inclusion\nin future blocks accordingly."),(0,o.kt)("h2",{id:"transaction-fees"},"Transaction Fees"),(0,o.kt)("p",null,"We use the model described above to set the fee level of a transaction based\non three parameters: its type, its on-chain length, and its expected resource usage. This fee\ndifferentiation is used to reflect the different costs that a transaction incurs on the network and\non the state, and to encourage the processing of certain types of transactions over others. A\nfraction of every transaction fee is paid to the block producer, while another fraction goes to\nfinance the Treasury. We highlight that, for a block producer, the rewards coming\nfrom transaction fees may constitute only a small fraction of their overall revenue, just enough to\nincentivise inclusion on the block.\nWe also run an adaptive transaction fee schedule that reacts to the traffic level, and ensures\nthat blocks are typically far from full, so that peaks of activity can be dealt with effectively and\nlong inclusion times are rare. In particular, the fee of each transaction is multiplied by a parameter\nthat evolves over time depending on the current network traffic.\nWe make fees evolve slowly enough, so that the fee of any transaction can be predicted accu-\nrately within a frame of an hour. In particular, we do not intend for transaction fees to be the\nmain source of income for stakers."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4b1e850b.57a51f36.js b/assets/js/4b1e850b.57a51f36.js new file mode 100644 index 000000000..41d83f917 --- /dev/null +++ b/assets/js/4b1e850b.57a51f36.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[8341],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>m});var a=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function n(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function s(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=a.createContext({}),u=function(e){var t=a.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},d=function(e){var t=u(e.components);return a.createElement(l.Provider,{value:t},e.children)},c="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},y=a.forwardRef((function(e,t){var r=e.components,o=e.mdxType,n=e.originalType,l=e.parentName,d=i(e,["components","mdxType","originalType","parentName"]),c=u(r),y=o,m=c["".concat(l,".").concat(y)]||c[y]||p[y]||n;return r?a.createElement(m,s(s({ref:t},d),{},{components:r})):a.createElement(m,s({ref:t},d))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var n=r.length,s=new Array(n);s[0]=y;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i[c]="string"==typeof e?e:o,s[1]=i;for(var u=2;u{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>n,metadata:()=>i,toc:()=>u});var a=r(7462),o=(r(7294),r(3905));const n={id:"treasury"},s="Treasury",i={unversionedId:"Polkadot/Module2/treasury",id:"Polkadot/Module2/treasury",title:"Treasury",description:"The Treasury raises funds continually. These funds are used to pay for developers that provide",source:"@site/docs/Polkadot/Module2/treasury.md",sourceDirName:"Polkadot/Module2",slug:"/Polkadot/Module2/treasury",permalink:"/docs/Polkadot/Module2/treasury",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module2/treasury.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"treasury"},sidebar:"polkadot",previous:{title:"On-Chain Governance",permalink:"/docs/Polkadot/Module2/governance"},next:{title:"Polkadot Network Consensus",permalink:"/docs/Polkadot/Module3/consensus"}},l={},u=[{value:"Polkadot Treasury Overview",id:"polkadot-treasury-overview",level:2},{value:"Treasury Proposals and Bounties",id:"treasury-proposals-and-bounties",level:2},{value:"Polkadot Treasury Payouts",id:"polkadot-treasury-payouts",level:2},{value:"Kusama Treasury",id:"kusama-treasury",level:2}],d={toc:u},c="wrapper";function p(e){let{components:t,...r}=e;return(0,o.kt)(c,(0,a.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"treasury"},"Treasury"),(0,o.kt)("p",null,"The Treasury raises funds continually. These funds are used to pay for developers that provide\nsoftware updates, apply any changes decided by referenda, adjust parameters, and generally keep\nthe system running smoothly. Funds may also be used for further goals such as marketing activities,\ncommunity events and outreach. This is ultimately controlled by all DOT holders via Governance\nand it will be the community and their collective imagination and judgment which really determines\nthe course of the Treasury.\nFunds for Treasury are raised in two ways:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"by channeling some of the validator rewards that come from minting of new tokens, and"),(0,o.kt)("li",{parentName:"ol"},"by channeling a fraction of transaction fees and of slashings.\nThe first method allows us to maintain a fixed in\nation rate while simultaneously having the\nvalidator rewards be dependent of the staking level: the difference between the\nscheduled minted tokens and the validator rewards is assigned to Treasury in each era. We also\nargue that it is convenient to have a fraction of all slashings be redirected to Treasury: following an\nevent that produced heavy stake slashing, the system is likely to need additional funds to develop\nsoftware updates or new infrastructure that deal with an existing issue, or it might be decided by\nGovernance to reimburse some of the slashed stake. Thus, it makes sense to have the slashed DOTs\navailable in Treasury, instead of burning them and having to mint more DOTs soon thereafter.")),(0,o.kt)("h2",{id:"polkadot-treasury-overview"},"Polkadot Treasury Overview"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/YEKJocpHsEE",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,o.kt)("h2",{id:"treasury-proposals-and-bounties"},"Treasury Proposals and Bounties"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/H7S4hWLan58",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,o.kt)("h2",{id:"polkadot-treasury-payouts"},"Polkadot Treasury Payouts"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/zwiqnXbloCA",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,o.kt)("h2",{id:"kusama-treasury"},"Kusama Treasury"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/OjMkv3OasUU",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4b1e850b.9c39ccf0.js b/assets/js/4b1e850b.9c39ccf0.js deleted file mode 100644 index 3d46fe227..000000000 --- a/assets/js/4b1e850b.9c39ccf0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[8341],{3905:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>m});var a=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function n(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function s(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=a.createContext({}),u=function(e){var t=a.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},d=function(e){var t=u(e.components);return a.createElement(l.Provider,{value:t},e.children)},c="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},y=a.forwardRef((function(e,t){var r=e.components,o=e.mdxType,n=e.originalType,l=e.parentName,d=i(e,["components","mdxType","originalType","parentName"]),c=u(r),y=o,m=c["".concat(l,".").concat(y)]||c[y]||p[y]||n;return r?a.createElement(m,s(s({ref:t},d),{},{components:r})):a.createElement(m,s({ref:t},d))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var n=r.length,s=new Array(n);s[0]=y;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i[c]="string"==typeof e?e:o,s[1]=i;for(var u=2;u{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>n,metadata:()=>i,toc:()=>u});var a=r(7462),o=(r(7294),r(3905));const n={id:"treasury"},s="Treasury",i={unversionedId:"Polkadot/Module2/treasury",id:"Polkadot/Module2/treasury",title:"Treasury",description:"The Treasury raises funds continually. These funds are used to pay for developers that provide",source:"@site/docs/Polkadot/Module2/treasury.md",sourceDirName:"Polkadot/Module2",slug:"/Polkadot/Module2/treasury",permalink:"/docs/Polkadot/Module2/treasury",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module2/treasury.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1654881714,formattedLastUpdatedAt:"Jun 10, 2022",frontMatter:{id:"treasury"},sidebar:"polkadot",previous:{title:"On-Chain Governance",permalink:"/docs/Polkadot/Module2/governance"},next:{title:"Polkadot Network Consensus",permalink:"/docs/Polkadot/Module3/consensus"}},l={},u=[{value:"Polkadot Treasury Overview",id:"polkadot-treasury-overview",level:2},{value:"Treasury Proposals and Bounties",id:"treasury-proposals-and-bounties",level:2},{value:"Polkadot Treasury Payouts",id:"polkadot-treasury-payouts",level:2},{value:"Kusama Treasury",id:"kusama-treasury",level:2}],d={toc:u},c="wrapper";function p(e){let{components:t,...r}=e;return(0,o.kt)(c,(0,a.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"treasury"},"Treasury"),(0,o.kt)("p",null,"The Treasury raises funds continually. These funds are used to pay for developers that provide\nsoftware updates, apply any changes decided by referenda, adjust parameters, and generally keep\nthe system running smoothly. Funds may also be used for further goals such as marketing activities,\ncommunity events and outreach. This is ultimately controlled by all DOT holders via Governance\nand it will be the community and their collective imagination and judgment which really determines\nthe course of the Treasury.\nFunds for Treasury are raised in two ways:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"by channeling some of the validator rewards that come from minting of new tokens, and"),(0,o.kt)("li",{parentName:"ol"},"by channeling a fraction of transaction fees and of slashings.\nThe \u2000rst method allows us to maintain a \u2000xed in\nation rate while simultaneously having the\nvalidator rewards be dependent of the staking level: the difference between the\nscheduled minted tokens and the validator rewards is assigned to Treasury in each era. We also\nargue that it is convenient to have a fraction of all slashings be redirected to Treasury: following an\nevent that produced heavy stake slashing, the system is likely to need additional funds to develop\nsoftware updates or new infrastructure that deal with an existing issue, or it might be decided by\nGovernance to reimburse some of the slashed stake. Thus, it makes sense to have the slashed DOTs\navailable in Treasury, instead of burning them and having to mint more DOTs soon thereafter.")),(0,o.kt)("h2",{id:"polkadot-treasury-overview"},"Polkadot Treasury Overview"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/YEKJocpHsEE",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,o.kt)("h2",{id:"treasury-proposals-and-bounties"},"Treasury Proposals and Bounties"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/H7S4hWLan58",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,o.kt)("h2",{id:"polkadot-treasury-payouts"},"Polkadot Treasury Payouts"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/zwiqnXbloCA",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,o.kt)("h2",{id:"kusama-treasury"},"Kusama Treasury"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/OjMkv3OasUU",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/55a851e8.50ec5685.js b/assets/js/55a851e8.50ec5685.js deleted file mode 100644 index 927ce7135..000000000 --- a/assets/js/55a851e8.50ec5685.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[3708],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>m});var n=a(7294);function o(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t=0||(o[a]=e[a]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(o[a]=e[a])}return o}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},d=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},h="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var a=e.components,o=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),h=c(a),u=o,m=h["".concat(s,".").concat(u)]||h[u]||p[u]||r;return a?n.createElement(m,i(i({ref:t},d),{},{components:a})):n.createElement(m,i({ref:t},d))}));function m(e,t){var a=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=a.length,i=new Array(r);i[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[h]="string"==typeof e?e:o,i[1]=l;for(var c=2;c{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>p,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var n=a(7462),o=(a(7294),a(3905));const r={id:"maintainers"},i="Validators and Nominators",l={unversionedId:"Polkadot/Module3/maintainers",id:"Polkadot/Module3/maintainers",title:"Validators and Nominators",description:"Beyond distributing data, relay-chain nodes may perform certain protocol-level roles listed",source:"@site/docs/Polkadot/Module3/maintainers.md",sourceDirName:"Polkadot/Module3",slug:"/Polkadot/Module3/maintainers",permalink:"/docs/Polkadot/Module3/maintainers",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module3/maintainers.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1654881714,formattedLastUpdatedAt:"Jun 10, 2022",frontMatter:{id:"maintainers"},sidebar:"polkadot",previous:{title:"Nominated Proof of Staking",permalink:"/docs/Polkadot/Module3/npos"},next:{title:"Securing the Network",permalink:"/docs/Polkadot/Module3/sharedsecurity"}},s={},c=[{value:"Validators",id:"validators",level:2},{value:"Nominators",id:"nominators",level:2},{value:"Collators",id:"collators",level:2}],d={toc:c},h="wrapper";function p(e){let{components:t,...a}=e;return(0,o.kt)(h,(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"validators-and-nominators"},"Validators and Nominators"),(0,o.kt)("p",null,"Beyond distributing data, relay-chain nodes may perform certain protocol-level roles listed\nhere. Some of these roles have restrictions and conditions associated with them:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Validator: performs the bulk of the security work. Must be a full node of the relay chain.\nInteracts with parachain collators, but need not participate in a parachain as a full node.")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Nominator: stakeholder who backs and selects validator candidates. This can\nbe done from a light client, and they need not have any awareness of parachains.")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Collator: collects and submits parachain data to the relay chain, subject to protocol rules\ndescribed below. They are chosen as de\u2000ned by the parachain, and must be full nodes of it."))),(0,o.kt)("h2",{id:"validators"},"Validators"),(0,o.kt)("p",null,"A validator is the highest in charge and helps seal new blocks on the Polkadot\nnetwork. The validators role is contingent upon a su\u2000ciently high bond being deposited, though\nwe allow other bonded parties to nominate one or more validators to act for them and as such\nsome portion of the validators bond may not necessarily be owned by the validator itself but\nrather by these nominators. A validator must run a relay-chain client implementation with high\navailability and bandwidth. At each block the node must be ready to accept the role of ratifying\na new block on some parachain, and may be required to double check a few more. This process\ninvolves receiving, validating and republishing candidate blocks. The parachain assignment is\nrandom and changes frequently. Since the validator cannot reasonably be expected to maintain a\nfully-synchronised database of all parachains, the task of devising a suggested new parachain block\nwill be delegated to a third-party, known as a collator. Once all new parachain blocks have been\nproperly rati\u2000ed by their appointed validator subgroups, validators must then ratify the relay-chain\nblock itself. This involves updating the state of the transaction queues (essentially moving data\nfrom a parachains output queue to another parachains input queue), processing the transactions of\nthe rati\u2000ed relay-chain transaction set and ratifying the \u2000nal block, including the \u2000nal parachain\nchanges. A validator provably not ful\u2000lling their role will be slashed i.e. part or all of their bond\nwill be taken. In some sense, validators are similar to the mining pools of current PoW blockchains."),(0,o.kt)("h2",{id:"nominators"},"Nominators"),(0,o.kt)("p",null,"A nominator is a stake-holding party who contributes to the security bond of a\nvalidator. They have no additional role except to place risk capital and as such to signal that they\ntrust a particular validator (or set thereof) to act responsibly in their maintenance of the network.\nThey receive a pro-rata increase or reduction in their deposit according to the bonds growth to\nwhich they contribute. Together with collators, next, nominators are in some sense similar to the\nminers of the present-day PoW networks."),(0,o.kt)("h2",{id:"collators"},"Collators"),(0,o.kt)("p",null,"Transaction collators (collators for short) are parties who assist validators in producing valid parachain blocks.\nThey maintain a full-node for a particular parachain; meaning that\nthey retain all necessary information to be able to author new blocks and execute transactions in\nmuch the same way as block producers do on current blockchains. Under normal circumstances,\nthey will collate and execute transactions to create an unsealed block, and provide it, together\nwith a proof of validity, to one or more validators presently responsible for proposing a parachain\nblock."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/55a851e8.5fe8eea0.js b/assets/js/55a851e8.5fe8eea0.js new file mode 100644 index 000000000..824f6d912 --- /dev/null +++ b/assets/js/55a851e8.5fe8eea0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[3708],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>m});var n=a(7294);function o(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t=0||(o[a]=e[a]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(o[a]=e[a])}return o}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},d=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},h="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var a=e.components,o=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),h=c(a),u=o,m=h["".concat(s,".").concat(u)]||h[u]||p[u]||r;return a?n.createElement(m,i(i({ref:t},d),{},{components:a})):n.createElement(m,i({ref:t},d))}));function m(e,t){var a=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=a.length,i=new Array(r);i[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[h]="string"==typeof e?e:o,i[1]=l;for(var c=2;c{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>p,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var n=a(7462),o=(a(7294),a(3905));const r={id:"maintainers"},i="Validators and Nominators",l={unversionedId:"Polkadot/Module3/maintainers",id:"Polkadot/Module3/maintainers",title:"Validators and Nominators",description:"Beyond distributing data, relay-chain nodes may perform certain protocol-level roles listed",source:"@site/docs/Polkadot/Module3/maintainers.md",sourceDirName:"Polkadot/Module3",slug:"/Polkadot/Module3/maintainers",permalink:"/docs/Polkadot/Module3/maintainers",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module3/maintainers.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"maintainers"},sidebar:"polkadot",previous:{title:"Nominated Proof of Staking",permalink:"/docs/Polkadot/Module3/npos"},next:{title:"Securing the Network",permalink:"/docs/Polkadot/Module3/sharedsecurity"}},s={},c=[{value:"Validators",id:"validators",level:2},{value:"Nominators",id:"nominators",level:2},{value:"Collators",id:"collators",level:2}],d={toc:c},h="wrapper";function p(e){let{components:t,...a}=e;return(0,o.kt)(h,(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"validators-and-nominators"},"Validators and Nominators"),(0,o.kt)("p",null,"Beyond distributing data, relay-chain nodes may perform certain protocol-level roles listed\nhere. Some of these roles have restrictions and conditions associated with them:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Validator: performs the bulk of the security work. Must be a full node of the relay chain.\nInteracts with parachain collators, but need not participate in a parachain as a full node.")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Nominator: stakeholder who backs and selects validator candidates. This can\nbe done from a light client, and they need not have any awareness of parachains.")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"Collator: collects and submits parachain data to the relay chain, subject to protocol rules\ndescribed below. They are chosen as defined by the parachain, and must be full nodes of it."))),(0,o.kt)("h2",{id:"validators"},"Validators"),(0,o.kt)("p",null,"A validator is the highest in charge and helps seal new blocks on the Polkadot\nnetwork. The validators role is contingent upon a sufficiently high bond being deposited, though\nwe allow other bonded parties to nominate one or more validators to act for them and as such\nsome portion of the validators bond may not necessarily be owned by the validator itself but\nrather by these nominators. A validator must run a relay-chain client implementation with high\navailability and bandwidth. At each block the node must be ready to accept the role of ratifying\na new block on some parachain, and may be required to double check a few more. This process\ninvolves receiving, validating and republishing candidate blocks. The parachain assignment is\nrandom and changes frequently. Since the validator cannot reasonably be expected to maintain a\nfully-synchronised database of all parachains, the task of devising a suggested new parachain block\nwill be delegated to a third-party, known as a collator. Once all new parachain blocks have been\nproperly ratified by their appointed validator subgroups, validators must then ratify the relay-chain\nblock itself. This involves updating the state of the transaction queues (essentially moving data\nfrom a parachains output queue to another parachains input queue), processing the transactions of\nthe ratified relay-chain transaction set and ratifying the final block, including the final parachain\nchanges. A validator provably not fulfilling their role will be slashed i.e. part or all of their bond\nwill be taken. In some sense, validators are similar to the mining pools of current PoW blockchains."),(0,o.kt)("h2",{id:"nominators"},"Nominators"),(0,o.kt)("p",null,"A nominator is a stake-holding party who contributes to the security bond of a\nvalidator. They have no additional role except to place risk capital and as such to signal that they\ntrust a particular validator (or set thereof) to act responsibly in their maintenance of the network.\nThey receive a pro-rata increase or reduction in their deposit according to the bonds growth to\nwhich they contribute. Together with collators, next, nominators are in some sense similar to the\nminers of the present-day PoW networks."),(0,o.kt)("h2",{id:"collators"},"Collators"),(0,o.kt)("p",null,"Transaction collators (collators for short) are parties who assist validators in producing valid parachain blocks.\nThey maintain a full-node for a particular parachain; meaning that\nthey retain all necessary information to be able to author new blocks and execute transactions in\nmuch the same way as block producers do on current blockchains. Under normal circumstances,\nthey will collate and execute transactions to create an unsealed block, and provide it, together\nwith a proof of validity, to one or more validators presently responsible for proposing a parachain\nblock."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/66f9d3f7.5377e133.js b/assets/js/66f9d3f7.5377e133.js deleted file mode 100644 index 9b32678b6..000000000 --- a/assets/js/66f9d3f7.5377e133.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[1004],{3905:(e,a,t)=>{t.d(a,{Zo:()=>h,kt:()=>f});var o=t(7294);function n(e,a,t){return a in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function i(e,a){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);a&&(o=o.filter((function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable}))),t.push.apply(t,o)}return t}function r(e){for(var a=1;a=0||(n[t]=e[t]);return n}(e,a);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(n[t]=e[t])}return n}var s=o.createContext({}),c=function(e){var a=o.useContext(s),t=a;return e&&(t="function"==typeof e?e(a):r(r({},a),e)),t},h=function(e){var a=c(e.components);return o.createElement(s.Provider,{value:a},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var a=e.children;return o.createElement(o.Fragment,{},a)}},u=o.forwardRef((function(e,a){var t=e.components,n=e.mdxType,i=e.originalType,s=e.parentName,h=l(e,["components","mdxType","originalType","parentName"]),d=c(t),u=n,f=d["".concat(s,".").concat(u)]||d[u]||p[u]||i;return t?o.createElement(f,r(r({ref:a},h),{},{components:t})):o.createElement(f,r({ref:a},h))}));function f(e,a){var t=arguments,n=a&&a.mdxType;if("string"==typeof e||n){var i=t.length,r=new Array(i);r[0]=u;var l={};for(var s in a)hasOwnProperty.call(a,s)&&(l[s]=a[s]);l.originalType=e,l[d]="string"==typeof e?e:n,r[1]=l;for(var c=2;c{t.r(a),t.d(a,{assets:()=>s,contentTitle:()=>r,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var o=t(7462),n=(t(7294),t(3905));const i={id:"parachainblock"},r="Path of a Parachain Block",l={unversionedId:"Polkadot/Module4/parachainblock",id:"Polkadot/Module4/parachainblock",title:"Path of a Parachain Block",description:"Collators watch the progress of the block-producing and consensus protocols, e.g. by participating",source:"@site/docs/Polkadot/Module4/parachainblock.md",sourceDirName:"Polkadot/Module4",slug:"/Polkadot/Module4/parachainblock",permalink:"/docs/Polkadot/Module4/parachainblock",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module4/parachainblock.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1656502743,formattedLastUpdatedAt:"Jun 29, 2022",frontMatter:{id:"parachainblock"},sidebar:"polkadot",previous:{title:"Nodes on Polkadot Network",permalink:"/docs/Polkadot/Module4/nodes"},next:{title:"Decentralization of Network",permalink:"/docs/Polkadot/Module4/decentralization"}},s={},c=[{value:"Parachains in Action",id:"parachains-in-action",level:2}],h={toc:c},d="wrapper";function p(e){let{components:a,...t}=e;return(0,n.kt)(d,(0,o.Z)({},h,t,{components:a,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"path-of-a-parachain-block"},"Path of a Parachain Block"),(0,n.kt)("p",null,"Collators watch the progress of the block-producing and consensus protocols, e.g. by participating\nin the relay chain as a full node. Based on what they think is the latest relay chain block that will\nmost likely be \u2000nalised, they build on top of the latest parachain block (or other data) that would be \u2000\nfinalised by it."),(0,n.kt)("p",null,"Collators sign data building on top of said latest parachain block, and submit it possibly\nindirectly, to the validators assigned to their parachain (parachain validators for short),\nfor inclusion in the relay chain. Ideally they submit a unique one, to help performance."),(0,n.kt)("p",null,"The parachain validators decide which parachain block to support, and presents relevant\ndata of it as a parachain's next candidate to be added to the next relay chain block."),(0,n.kt)("p",null,"A block-producing validator collects candidates from all parachains, and puts this collection\nalong with any recent relay chain extrinsics into a relay chain head block.\nFor performance, this does not contain the full data from all parachains, but only metadata\nand partial data, including security-related metadata."),(0,n.kt)("p",null,"In the unfavourable case, this can result in forks, resolved later. This subprotocol\nis designed so that even with forks, participants have an idea of the block most likely to be\nfinalised, similar to Proof-of-Work protocols."),(0,n.kt)("p",null,"A subprotocol is run to ensure that the full data is indeed available, including and distributing\nit to various other relay-chain nodes."),(0,n.kt)("p",null,"Data submitted from a parachain might include indications that they are sending messages\nto another parachain, including metadata to facilitate this. This is now included on the relay\nchain head(s), so recipient parachains are aware of which new messages have been sent to\nthem. They now retrieve the message bodies from the sending parachains."),(0,n.kt)("p",null,"Validators submit their votes on the block and \u2000nalises it, resolving any forks to a single\nhead. These votes are added to the relay chain blocks."),(0,n.kt)("h2",{id:"parachains-in-action"},"Parachains in Action"),(0,n.kt)("p",null,"In outline, a collator produces a parachain block, sends it to the parachain validators, who sign\nits header as valid, and the header with enough signatures is placed on the relay chain. At this\npoint, the parachain block is as canonical as the relay chain block its header appeared in. If this\nrelay chain block is in the best chain according to BABE, so is the parachain\nblock and when this relay chain block is \u2000nalised, so is the parachain block.\nBecause the parachain validators switch parachains frequently, they are stateless clients of\nthe parachain. Thus we distinguish between the parachain block B, which is normally enough\nfor full nodes of the parachain such as collators to update the parachain state, and the Proof of\nValidity(PoV) block BPoV , which a validator who does not have the parachain state can verify.\nAny validator should be able to verify BPoV given the relay chain state using the parachain's\nparachain validation function (PVF), the Web assembly code for which is stored on the\nrelay chain in a similar way to the relay chain's runtime. The STVF takes as an input the PoV\nblock, the header of the last parachain block from this parachain and a small amount of data from\nthe relay chain state.\nThe STVF outputs the validity of the block, the header of this block and its outgoing mes-\nsages. The PoV block contains any outgoing messages and the parachain block B. The parachain\nvalidators should gossip the parachain block to the parachain network, as a back up to the collator\nitself doing so.\nThe PoV block will be the parachain block, its outgoing messages, its header and light client\nproof witnesses. These witnesses are Merkle proofs that give all elements of the input and output\nstate that are used or modi\u2000ed by the state transition from the input and output state roots.\nTo aid in censorship resistance, a parachain may want to use proof of work or proof of stake to\nselect collators, where the selection strategy is up to the given parachain. This can be implemented\nin the STVF and need not be a part of the Polkadot protocol. So for proof of work, the STVF would\ncheck that the hash of the block is su\u2000ciently small. However, for speed, it would be useful to\nensure that most relay chain blocks can include a parachain block. For PoW, this would necessitate\nit being probable that multiple collators are allowed to produce a block. As such we will still need\na tie-breaker for the parachain validators to coordinate on validating the same parachain block\n\u2000rst. This may be the golden ticket scheme of. For proof of stake this may not be necessary."),(0,n.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/AlnjFIfwOH0",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/66f9d3f7.5c664fe1.js b/assets/js/66f9d3f7.5c664fe1.js new file mode 100644 index 000000000..4dcf9b495 --- /dev/null +++ b/assets/js/66f9d3f7.5c664fe1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[1004],{3905:(e,a,t)=>{t.d(a,{Zo:()=>h,kt:()=>f});var o=t(7294);function n(e,a,t){return a in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function i(e,a){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);a&&(o=o.filter((function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable}))),t.push.apply(t,o)}return t}function r(e){for(var a=1;a=0||(n[t]=e[t]);return n}(e,a);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(n[t]=e[t])}return n}var s=o.createContext({}),c=function(e){var a=o.useContext(s),t=a;return e&&(t="function"==typeof e?e(a):r(r({},a),e)),t},h=function(e){var a=c(e.components);return o.createElement(s.Provider,{value:a},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var a=e.children;return o.createElement(o.Fragment,{},a)}},u=o.forwardRef((function(e,a){var t=e.components,n=e.mdxType,i=e.originalType,s=e.parentName,h=l(e,["components","mdxType","originalType","parentName"]),d=c(t),u=n,f=d["".concat(s,".").concat(u)]||d[u]||p[u]||i;return t?o.createElement(f,r(r({ref:a},h),{},{components:t})):o.createElement(f,r({ref:a},h))}));function f(e,a){var t=arguments,n=a&&a.mdxType;if("string"==typeof e||n){var i=t.length,r=new Array(i);r[0]=u;var l={};for(var s in a)hasOwnProperty.call(a,s)&&(l[s]=a[s]);l.originalType=e,l[d]="string"==typeof e?e:n,r[1]=l;for(var c=2;c{t.r(a),t.d(a,{assets:()=>s,contentTitle:()=>r,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var o=t(7462),n=(t(7294),t(3905));const i={id:"parachainblock"},r="Path of a Parachain Block",l={unversionedId:"Polkadot/Module4/parachainblock",id:"Polkadot/Module4/parachainblock",title:"Path of a Parachain Block",description:"Collators watch the progress of the block-producing and consensus protocols, e.g. by participating",source:"@site/docs/Polkadot/Module4/parachainblock.md",sourceDirName:"Polkadot/Module4",slug:"/Polkadot/Module4/parachainblock",permalink:"/docs/Polkadot/Module4/parachainblock",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module4/parachainblock.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"parachainblock"},sidebar:"polkadot",previous:{title:"Nodes on Polkadot Network",permalink:"/docs/Polkadot/Module4/nodes"},next:{title:"Decentralization of Network",permalink:"/docs/Polkadot/Module4/decentralization"}},s={},c=[{value:"Parachains in Action",id:"parachains-in-action",level:2}],h={toc:c},d="wrapper";function p(e){let{components:a,...t}=e;return(0,n.kt)(d,(0,o.Z)({},h,t,{components:a,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"path-of-a-parachain-block"},"Path of a Parachain Block"),(0,n.kt)("p",null,"Collators watch the progress of the block-producing and consensus protocols, e.g. by participating\nin the relay chain as a full node. Based on what they think is the latest relay chain block that will\nmost likely be finalised, they build on top of the latest parachain block (or other data) that would be\nfinalised by it."),(0,n.kt)("p",null,"Collators sign data building on top of said latest parachain block, and submit it possibly\nindirectly, to the validators assigned to their parachain (parachain validators for short),\nfor inclusion in the relay chain. Ideally they submit a unique one, to help performance."),(0,n.kt)("p",null,"The parachain validators decide which parachain block to support, and presents relevant\ndata of it as a parachain's next candidate to be added to the next relay chain block."),(0,n.kt)("p",null,"A block-producing validator collects candidates from all parachains, and puts this collection\nalong with any recent relay chain extrinsics into a relay chain head block.\nFor performance, this does not contain the full data from all parachains, but only metadata\nand partial data, including security-related metadata."),(0,n.kt)("p",null,"In the unfavourable case, this can result in forks, resolved later. This subprotocol\nis designed so that even with forks, participants have an idea of the block most likely to be\nfinalised, similar to Proof-of-Work protocols."),(0,n.kt)("p",null,"A subprotocol is run to ensure that the full data is indeed available, including and distributing\nit to various other relay-chain nodes."),(0,n.kt)("p",null,"Data submitted from a parachain might include indications that they are sending messages\nto another parachain, including metadata to facilitate this. This is now included on the relay\nchain head(s), so recipient parachains are aware of which new messages have been sent to\nthem. They now retrieve the message bodies from the sending parachains."),(0,n.kt)("p",null,"Validators submit their votes on the block and finalises it, resolving any forks to a single\nhead. These votes are added to the relay chain blocks."),(0,n.kt)("h2",{id:"parachains-in-action"},"Parachains in Action"),(0,n.kt)("p",null,"In outline, a collator produces a parachain block, sends it to the parachain validators, who sign\nits header as valid, and the header with enough signatures is placed on the relay chain. At this\npoint, the parachain block is as canonical as the relay chain block its header appeared in. If this\nrelay chain block is in the best chain according to BABE, so is the parachain\nblock and when this relay chain block is finalised, so is the parachain block.\nBecause the parachain validators switch parachains frequently, they are stateless clients of\nthe parachain. Thus we distinguish between the parachain block B, which is normally enough\nfor full nodes of the parachain such as collators to update the parachain state, and the Proof of\nValidity(PoV) block BPoV , which a validator who does not have the parachain state can verify.\nAny validator should be able to verify BPoV given the relay chain state using the parachain's\nparachain validation function (PVF), the Web assembly code for which is stored on the\nrelay chain in a similar way to the relay chain's runtime. The STVF takes as an input the PoV\nblock, the header of the last parachain block from this parachain and a small amount of data from\nthe relay chain state.\nThe STVF outputs the validity of the block, the header of this block and its outgoing mes-\nsages. The PoV block contains any outgoing messages and the parachain block B. The parachain\nvalidators should gossip the parachain block to the parachain network, as a back up to the collator\nitself doing so.\nThe PoV block will be the parachain block, its outgoing messages, its header and light client\nproof witnesses. These witnesses are Merkle proofs that give all elements of the input and output\nstate that are used or modified by the state transition from the input and output state roots.\nTo aid in censorship resistance, a parachain may want to use proof of work or proof of stake to\nselect collators, where the selection strategy is up to the given parachain. This can be implemented\nin the STVF and need not be a part of the Polkadot protocol. So for proof of work, the STVF would\ncheck that the hash of the block is sufficiently small. However, for speed, it would be useful to\nensure that most relay chain blocks can include a parachain block. For PoW, this would necessitate\nit being probable that multiple collators are allowed to produce a block. As such we will still need\na tie-breaker for the parachain validators to coordinate on validating the same parachain block\nfirst. This may be the golden ticket scheme of. For proof of stake this may not be necessary."),(0,n.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/AlnjFIfwOH0",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/698a20df.108fd7b0.js b/assets/js/698a20df.108fd7b0.js deleted file mode 100644 index 6a6899836..000000000 --- a/assets/js/698a20df.108fd7b0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[3347],{3905:(e,t,o)=>{o.d(t,{Zo:()=>h,kt:()=>f});var a=o(7294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function r(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,a)}return o}function i(e){for(var t=1;t=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var l=a.createContext({}),d=function(e){var t=a.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},h=function(e){var t=d(e.components);return a.createElement(l.Provider,{value:t},e.children)},c="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var o=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,h=s(e,["components","mdxType","originalType","parentName"]),c=d(o),p=n,f=c["".concat(l,".").concat(p)]||c[p]||u[p]||r;return o?a.createElement(f,i(i({ref:t},h),{},{components:o})):a.createElement(f,i({ref:t},h))}));function f(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=o.length,i=new Array(r);i[0]=p;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[c]="string"==typeof e?e:n,i[1]=s;for(var d=2;d{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>d});var a=o(7462),n=(o(7294),o(3905));const r={id:"q-faq",title:"Q for FAQ",sidebar_position:16},i=void 0,s={unversionedId:"AtoZ/q-faq",id:"AtoZ/q-faq",title:"Q for FAQ",description:"J for Polkadot JS",source:"@site/docs/AtoZ/q-faq.md",sourceDirName:"AtoZ",slug:"/AtoZ/q-faq",permalink:"/docs/AtoZ/q-faq",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/AtoZ/q-faq.md",tags:[],version:"current",lastUpdatedBy:"filippoweb3",lastUpdatedAt:1677164999,formattedLastUpdatedAt:"Feb 23, 2023",sidebarPosition:16,frontMatter:{id:"q-faq",title:"Q for FAQ",sidebar_position:16},sidebar:"atoz",previous:{title:"P for Phragm\xe9n",permalink:"/docs/AtoZ/phragm\xe9n"}},l={},d=[{value:"How does staking work?",id:"how-does-staking-work",level:2},{value:"What is the Polkadot-JS UI?",id:"what-is-the-polkadot-js-ui",level:2},{value:"Why does a parachain need to connect to the Relay Chain?",id:"why-does-a-parachain-need-to-connect-to-the-relay-chain",level:2},{value:"How is Polkadot different from Cosmos?",id:"how-is-polkadot-different-from-cosmos",level:2},{value:"Why do different networks have different addresses but the same pubkey?",id:"why-do-different-networks-have-different-addresses-but-the-same-pubkey",level:2},{value:"Why is the unbonding period 28 days and why can\u2019t I earn staking rewards when I\u2019m unbonding?",id:"why-is-the-unbonding-period-28-days-and-why-cant-i-earn-staking-rewards-when-im-unbonding",level:2},{value:"Is Kusama a testnet?",id:"is-kusama-a-testnet",level:2}],h={toc:d},c="wrapper";function u(e){let{components:t,...r}=e;return(0,n.kt)(c,(0,a.Z)({},h,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"J for Polkadot JS",src:o(7774).Z,width:"1600",height:"840"})),(0,n.kt)("p",null,"For the past two-plus years that Polkadot has been live, questions from the community are frequently present in socials. In this post, I\u2019ll be answering some of those questions. If you have any other questions that you think should be a part of this post, please leave a comment."),(0,n.kt)("h2",{id:"how-does-staking-work"},"How does staking work?"),(0,n.kt)("p",null,"Staking on Polkadot uses Nominated Proof-of-Stake(NPoS), a flavor of PoS that allows for two types of participants, nominators and validators. Validators are the entities that run a full version of the Polkadot blockchain as a node, and they secure the network by bonding a number of tokens, which in turn allows them to create blocks. Nominators are the entities that elect validators into the active validator set, which currently is 297 on Polkadot and 1000 on Kusama. "),(0,n.kt)("admonition",{type:"info"},(0,n.kt)("p",{parentName:"admonition"},"The active validator set is an arbitrary value that can be changed via governance.")),(0,n.kt)("p",null,"The role of staking is a part of the consensus mechanism. The mechanism allows token holders to be the on-chain entities that secure the network by putting up their tokens as collateral. This incentive, baked into the protocol, allows any token holder to earn newly created tokens, which the network mints whenever there is a successful new block. In simple terms, this is the inflation portion of the monetary policy of the network."),(0,n.kt)("p",null,"The reason why its design is complex is due to avoid the pitfalls of staking models. Mainly that of a few entities controlling the majority of the stake. Proof-of-Stake systems all have a different flavor of choosing the staked entities as validators. And the goal here is not to favor certain entities more than others. That endevour in itself is a challenging problem to solve. "),(0,n.kt)("p",null,"For a detailed dive into NPoS read the ",(0,n.kt)("a",{parentName:"p",href:"/docs/AtoZ/npos"},"letter N")," post of this blog series. And also be sure to checkout the ",(0,n.kt)("a",{parentName:"p",href:"https://wiki.polkadot.network/docs/learn-phragmen"},"Polkadot wiki"),"."),(0,n.kt)("h2",{id:"what-is-the-polkadot-js-ui"},"What is the Polkadot-JS UI?"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"/docs/AtoZ/polkadot-js"},"Polkadot-JS")," is a developer-centric interface that allows for granular control of Substrate-based chains. The idea with Polkadot-JS is to be a place where all ",(0,n.kt)("a",{parentName:"p",href:"https://wiki.polkadot.network/docs/learn-extrinsics"},"extrinsics")," of all pallets can be engaged. For a tool like Polkadot-JS to stay up to date with the ever-changing Substrate landscape, functionality is the primary goal, and the user interface has to be a secondary consideration. Therefore, we should consider Polkadot-JS as a featureful tool rather than a user-centric one. For more user-centric tools, try one of the many wallets that support Substrate."),(0,n.kt)("h2",{id:"why-does-a-parachain-need-to-connect-to-the-relay-chain"},"Why does a parachain need to connect to the Relay Chain?"),(0,n.kt)("p",null,"The Relay Chain provides security for parachains. On a more granular level, it also provides a marketplace for parachains to compete. Via the parachain auctions, this competition can be considered healthy, as it incentivizes good product development and disincentivizes scams. The security of the Relay Chain is inherited by the parachains, making them as secure as the Relay Chain. This is a big improvement to previous models of blockchain development - as previously blockchains would have needed to develop their network security from scratch. "),(0,n.kt)("h2",{id:"how-is-polkadot-different-from-cosmos"},"How is Polkadot different from Cosmos?"),(0,n.kt)("p",null,"A classic comparison. Polkadot vs. Cosmos has been one of the most debated topics as they are competitors for a chain model that allows other layer 1 chains to connect and interoperate. See ",(0,n.kt)("a",{parentName:"p",href:"https://wiki.polkadot.network/docs/learn-comparisons-cosmos"},"this wiki page")," for more info about this topic."),(0,n.kt)("h2",{id:"why-do-different-networks-have-different-addresses-but-the-same-pubkey"},"Why do different networks have different addresses but the same pubkey?"),(0,n.kt)("p",null,"Substrate-based chains use the SS58 format for generating their account keypairs. Different network formats are other representations of the same public key in a key pair generated by an address-generation tool. This results in addresses being compatible across networks as long as the format is converted correctly."),(0,n.kt)("h2",{id:"why-is-the-unbonding-period-28-days-and-why-cant-i-earn-staking-rewards-when-im-unbonding"},"Why is the unbonding period 28 days and why can\u2019t I earn staking rewards when I\u2019m unbonding?"),(0,n.kt)("p",null,"The unbonding feature is designed to ensure that those who stake are not simply able to remove their stake at will, which ensures security in the network. Technically you will earn staking rewards if you unbond in the middle of an era for that era; however, not for the following one. The time you will have to wait until your tokens serves as a cooldown. During this time, you cannot nominate and/or transfer, hence unable to earn staking rewards."),(0,n.kt)("h2",{id:"is-kusama-a-testnet"},"Is Kusama a testnet?"),(0,n.kt)("p",null,"Kusama was intended to be a value-bearing test network, but since its inception, we have seen it become a sovereign network in its own right. Including a vibrant developer community and culture, as well as projects that live solely in Kusama, with no plans to move on to Polkadot. So why? Because, in the world of blockchains, we are dealing with real value. A bug in runtime code can be extremely costly, and to prevent this, runtime code needs to be tested in real value-bearing environments. In addition to Web 2.0 style testing, Web 3.0 needs to be run in the wild to see if any game-theoretic unpredictable behaviors emerge. Kusama is exactly that for Polkadot and any other parachain that plans to be on Polkadot. But now, it is also home to many projects that have found a home in the world of Kusama."))}u.isMDXComponent=!0},7774:(e,t,o)=>{o.d(t,{Z:()=>a});const a=o.p+"assets/images/Q-2caee259075baa504c86aa870364f944.png"}}]); \ No newline at end of file diff --git a/assets/js/698a20df.cb6be3c1.js b/assets/js/698a20df.cb6be3c1.js new file mode 100644 index 000000000..b9d253cc7 --- /dev/null +++ b/assets/js/698a20df.cb6be3c1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[3347],{3905:(e,t,o)=>{o.d(t,{Zo:()=>h,kt:()=>f});var a=o(7294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function r(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,a)}return o}function i(e){for(var t=1;t=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var l=a.createContext({}),d=function(e){var t=a.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},h=function(e){var t=d(e.components);return a.createElement(l.Provider,{value:t},e.children)},c="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var o=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,h=s(e,["components","mdxType","originalType","parentName"]),c=d(o),p=n,f=c["".concat(l,".").concat(p)]||c[p]||u[p]||r;return o?a.createElement(f,i(i({ref:t},h),{},{components:o})):a.createElement(f,i({ref:t},h))}));function f(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=o.length,i=new Array(r);i[0]=p;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[c]="string"==typeof e?e:n,i[1]=s;for(var d=2;d{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>r,metadata:()=>s,toc:()=>d});var a=o(7462),n=(o(7294),o(3905));const r={id:"q-faq",title:"Q for FAQ",sidebar_position:16},i=void 0,s={unversionedId:"AtoZ/q-faq",id:"AtoZ/q-faq",title:"Q for FAQ",description:"J for Polkadot JS",source:"@site/docs/AtoZ/q-faq.md",sourceDirName:"AtoZ",slug:"/AtoZ/q-faq",permalink:"/docs/AtoZ/q-faq",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/AtoZ/q-faq.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692960998,formattedLastUpdatedAt:"Aug 25, 2023",sidebarPosition:16,frontMatter:{id:"q-faq",title:"Q for FAQ",sidebar_position:16},sidebar:"atoz",previous:{title:"P for Phragm\xe9n",permalink:"/docs/AtoZ/phragm\xe9n"}},l={},d=[{value:"How does staking work?",id:"how-does-staking-work",level:2},{value:"What is the Polkadot-JS UI?",id:"what-is-the-polkadot-js-ui",level:2},{value:"Why does a parachain need to connect to the Relay Chain?",id:"why-does-a-parachain-need-to-connect-to-the-relay-chain",level:2},{value:"How is Polkadot different from Cosmos?",id:"how-is-polkadot-different-from-cosmos",level:2},{value:"Why do different networks have different addresses but the same pubkey?",id:"why-do-different-networks-have-different-addresses-but-the-same-pubkey",level:2},{value:"Why is the unbonding period 28 days and why can\u2019t I earn staking rewards when I\u2019m unbonding?",id:"why-is-the-unbonding-period-28-days-and-why-cant-i-earn-staking-rewards-when-im-unbonding",level:2},{value:"Is Kusama a testnet?",id:"is-kusama-a-testnet",level:2}],h={toc:d},c="wrapper";function u(e){let{components:t,...r}=e;return(0,n.kt)(c,(0,a.Z)({},h,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"J for Polkadot JS",src:o(7774).Z,width:"1600",height:"840"})),(0,n.kt)("p",null,"For the past two-plus years that Polkadot has been live, questions from the community are frequently present in socials. In this post, I\u2019ll be answering some of those questions. If you have any other questions that you think should be a part of this post, please leave a comment."),(0,n.kt)("h2",{id:"how-does-staking-work"},"How does staking work?"),(0,n.kt)("p",null,"Staking on Polkadot uses Nominated Proof-of-Stake(NPoS), a flavor of PoS that allows for two types of participants, nominators and validators. Validators are the entities that run a full version of the Polkadot blockchain as a node, and they secure the network by bonding a number of tokens, which in turn allows them to create blocks. Nominators are the entities that elect validators into the active validator set, which currently is 297 on Polkadot and 1000 on Kusama."),(0,n.kt)("admonition",{type:"info"},(0,n.kt)("p",{parentName:"admonition"},"The active validator set is an arbitrary value that can be changed via governance.")),(0,n.kt)("p",null,"The role of staking is a part of the consensus mechanism. The mechanism allows token holders to be the on-chain entities that secure the network by putting up their tokens as collateral. This incentive, baked into the protocol, allows any token holder to earn newly created tokens, which the network mints whenever there is a successful new block. In simple terms, this is the inflation portion of the monetary policy of the network."),(0,n.kt)("p",null,"The reason why its design is complex is due to avoid the pitfalls of staking models. Mainly that of a few entities controlling the majority of the stake. Proof-of-Stake systems all have a different flavor of choosing the staked entities as validators. And the goal here is not to favor certain entities more than others. That endeavour in itself is a challenging problem to solve."),(0,n.kt)("p",null,"For a detailed dive into NPoS read the ",(0,n.kt)("a",{parentName:"p",href:"/docs/AtoZ/npos"},"letter N")," post of this blog series. And also be sure to checkout the ",(0,n.kt)("a",{parentName:"p",href:"https://wiki.polkadot.network/docs/learn-phragmen"},"Polkadot wiki"),"."),(0,n.kt)("h2",{id:"what-is-the-polkadot-js-ui"},"What is the Polkadot-JS UI?"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"/docs/AtoZ/polkadot-js"},"Polkadot-JS")," is a developer-centric interface that allows for granular control of Substrate-based chains. The idea with Polkadot-JS is to be a place where all ",(0,n.kt)("a",{parentName:"p",href:"https://wiki.polkadot.network/docs/learn-extrinsics"},"extrinsics")," of all pallets can be engaged. For a tool like Polkadot-JS to stay up to date with the ever-changing Substrate landscape, functionality is the primary goal, and the user interface has to be a secondary consideration. Therefore, we should consider Polkadot-JS as a featureful tool rather than a user-centric one. For more user-centric tools, try one of the many wallets that support Substrate."),(0,n.kt)("h2",{id:"why-does-a-parachain-need-to-connect-to-the-relay-chain"},"Why does a parachain need to connect to the Relay Chain?"),(0,n.kt)("p",null,"The Relay Chain provides security for parachains. On a more granular level, it also provides a marketplace for parachains to compete. Via the parachain auctions, this competition can be considered healthy, as it incentivizes good product development and disincentivizes scams. The security of the Relay Chain is inherited by the parachains, making them as secure as the Relay Chain. This is a big improvement to previous models of blockchain development - as previously blockchains would have needed to develop their network security from scratch."),(0,n.kt)("h2",{id:"how-is-polkadot-different-from-cosmos"},"How is Polkadot different from Cosmos?"),(0,n.kt)("p",null,"A classic comparison. Polkadot vs. Cosmos has been one of the most debated topics as they are competitors for a chain model that allows other layer 1 chains to connect and interoperate. See ",(0,n.kt)("a",{parentName:"p",href:"https://wiki.polkadot.network/docs/learn-comparisons-cosmos"},"this wiki page")," for more info about this topic."),(0,n.kt)("h2",{id:"why-do-different-networks-have-different-addresses-but-the-same-pubkey"},"Why do different networks have different addresses but the same pubkey?"),(0,n.kt)("p",null,"Substrate-based chains use the SS58 format for generating their account keypairs. Different network formats are other representations of the same public key in a key pair generated by an address-generation tool. This results in addresses being compatible across networks as long as the format is converted correctly."),(0,n.kt)("h2",{id:"why-is-the-unbonding-period-28-days-and-why-cant-i-earn-staking-rewards-when-im-unbonding"},"Why is the unbonding period 28 days and why can\u2019t I earn staking rewards when I\u2019m unbonding?"),(0,n.kt)("p",null,"The unbonding feature is designed to ensure that those who stake are not simply able to remove their stake at will, which ensures security in the network. Technically you will earn staking rewards if you unbond in the middle of an era for that era; however, not for the following one. The time you will have to wait until your tokens serves as a cooldown. During this time, you cannot nominate and/or transfer, hence unable to earn staking rewards."),(0,n.kt)("h2",{id:"is-kusama-a-testnet"},"Is Kusama a testnet?"),(0,n.kt)("p",null,"Kusama was intended to be a value-bearing test network, but since its inception, we have seen it become a sovereign network in its own right. Including a vibrant developer community and culture, as well as projects that live solely in Kusama, with no plans to move on to Polkadot. So why? Because, in the world of blockchains, we are dealing with real value. A bug in runtime code can be extremely costly, and to prevent this, runtime code needs to be tested in real value-bearing environments. In addition to Web 2.0 style testing, Web 3.0 needs to be run in the wild to see if any game-theoretic unpredictable behaviors emerge. Kusama is exactly that for Polkadot and any other parachain that plans to be on Polkadot. But now, it is also home to many projects that have found a home in the world of Kusama."))}u.isMDXComponent=!0},7774:(e,t,o)=>{o.d(t,{Z:()=>a});const a=o.p+"assets/images/Q-2caee259075baa504c86aa870364f944.png"}}]); \ No newline at end of file diff --git a/assets/js/845bb328.d9ddd61b.js b/assets/js/845bb328.57d0cd9a.js similarity index 66% rename from assets/js/845bb328.d9ddd61b.js rename to assets/js/845bb328.57d0cd9a.js index 35c15d9bf..9016cb869 100644 --- a/assets/js/845bb328.d9ddd61b.js +++ b/assets/js/845bb328.57d0cd9a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[1516],{3905:(t,e,r)=>{r.d(e,{Zo:()=>l,kt:()=>h});var a=r(7294);function n(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function o(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,a)}return r}function s(t){for(var e=1;e=0||(n[r]=t[r]);return n}(t,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(t,r)&&(n[r]=t[r])}return n}var c=a.createContext({}),u=function(t){var e=a.useContext(c),r=e;return t&&(r="function"==typeof t?t(e):s(s({},e),t)),r},l=function(t){var e=u(t.components);return a.createElement(c.Provider,{value:e},t.children)},b="mdxType",d={inlineCode:"code",wrapper:function(t){var e=t.children;return a.createElement(a.Fragment,{},e)}},p=a.forwardRef((function(t,e){var r=t.components,n=t.mdxType,o=t.originalType,c=t.parentName,l=i(t,["components","mdxType","originalType","parentName"]),b=u(r),p=n,h=b["".concat(c,".").concat(p)]||b[p]||d[p]||o;return r?a.createElement(h,s(s({ref:e},l),{},{components:r})):a.createElement(h,s({ref:e},l))}));function h(t,e){var r=arguments,n=e&&e.mdxType;if("string"==typeof t||n){var o=r.length,s=new Array(o);s[0]=p;var i={};for(var c in e)hasOwnProperty.call(e,c)&&(i[c]=e[c]);i.originalType=t,i[b]="string"==typeof t?t:n,s[1]=i;for(var u=2;u{r.r(e),r.d(e,{assets:()=>c,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>u});var a=r(7462),n=(r(7294),r(3905));const o={id:"substrate-history",title:"History Behind Substrate",sidebar_label:"History Behind Substrate",description:"Learn the history of how Substrate came about and its relevance to Polkadot."},s=void 0,i={unversionedId:"Substrate/section1/substrate-history",id:"Substrate/section1/substrate-history",title:"History Behind Substrate",description:"Learn the history of how Substrate came about and its relevance to Polkadot.",source:"@site/docs/Substrate/section1/substrate-history.md",sourceDirName:"Substrate/section1",slug:"/Substrate/section1/substrate-history",permalink:"/docs/Substrate/section1/substrate-history",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section1/substrate-history.md",tags:[],version:"current",lastUpdatedBy:"bader y",lastUpdatedAt:1688399601,formattedLastUpdatedAt:"Jul 3, 2023",frontMatter:{id:"substrate-history",title:"History Behind Substrate",sidebar_label:"History Behind Substrate",description:"Learn the history of how Substrate came about and its relevance to Polkadot."},sidebar:"substrate",previous:{title:"What is Substrate and FRAME?",permalink:"/docs/Substrate/section1/what-is-substrate"},next:{title:"Substrate\u2019s Design Choices",permalink:"/docs/Substrate/section1/substrate-design"}},c={},u=[],l={toc:u},b="wrapper";function d(t){let{components:e,...r}=t;return(0,n.kt)(b,(0,a.Z)({},l,r,{components:e,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"Substrate, at its origins, was created very close to the conception of the Polkadot network. The two are usually tightly coupled - where the improvements to Substrate funnel into Polkadot and vice versa. Parachains that connect to Polkadot are also built using Substrate, generally using some abstractions such as FRAME and Cumulus."),(0,n.kt)("p",null,"Setting the context is crucial to understand Susbtrate's role in web3. Polkadot is a relay chain that uses Substrate libraries and FRAME. Substrate is a blockchain developer framework. It enables the creation of solo chains or parachains. Initially, much of what was Substrate came from Polkadot due to necessity. The reasoning behind this choice was if parachains were to be built, it would be sensible to make all standard blockchain protocols and primitives available to parachain developers."))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[1516],{3905:(t,e,r)=>{r.d(e,{Zo:()=>l,kt:()=>h});var a=r(7294);function n(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function o(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,a)}return r}function s(t){for(var e=1;e=0||(n[r]=t[r]);return n}(t,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(t,r)&&(n[r]=t[r])}return n}var c=a.createContext({}),u=function(t){var e=a.useContext(c),r=e;return t&&(r="function"==typeof t?t(e):s(s({},e),t)),r},l=function(t){var e=u(t.components);return a.createElement(c.Provider,{value:e},t.children)},b="mdxType",d={inlineCode:"code",wrapper:function(t){var e=t.children;return a.createElement(a.Fragment,{},e)}},p=a.forwardRef((function(t,e){var r=t.components,n=t.mdxType,o=t.originalType,c=t.parentName,l=i(t,["components","mdxType","originalType","parentName"]),b=u(r),p=n,h=b["".concat(c,".").concat(p)]||b[p]||d[p]||o;return r?a.createElement(h,s(s({ref:e},l),{},{components:r})):a.createElement(h,s({ref:e},l))}));function h(t,e){var r=arguments,n=e&&e.mdxType;if("string"==typeof t||n){var o=r.length,s=new Array(o);s[0]=p;var i={};for(var c in e)hasOwnProperty.call(e,c)&&(i[c]=e[c]);i.originalType=t,i[b]="string"==typeof t?t:n,s[1]=i;for(var u=2;u{r.r(e),r.d(e,{assets:()=>c,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>u});var a=r(7462),n=(r(7294),r(3905));const o={id:"substrate-history",title:"History Behind Substrate",sidebar_label:"History Behind Substrate",description:"Learn the history of how Substrate came about and its relevance to Polkadot."},s=void 0,i={unversionedId:"Substrate/section1/substrate-history",id:"Substrate/section1/substrate-history",title:"History Behind Substrate",description:"Learn the history of how Substrate came about and its relevance to Polkadot.",source:"@site/docs/Substrate/section1/substrate-history.md",sourceDirName:"Substrate/section1",slug:"/Substrate/section1/substrate-history",permalink:"/docs/Substrate/section1/substrate-history",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section1/substrate-history.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692960998,formattedLastUpdatedAt:"Aug 25, 2023",frontMatter:{id:"substrate-history",title:"History Behind Substrate",sidebar_label:"History Behind Substrate",description:"Learn the history of how Substrate came about and its relevance to Polkadot."},sidebar:"substrate",previous:{title:"What is Substrate and FRAME?",permalink:"/docs/Substrate/section1/what-is-substrate"},next:{title:"Substrate\u2019s Design Choices",permalink:"/docs/Substrate/section1/substrate-design"}},c={},u=[],l={toc:u},b="wrapper";function d(t){let{components:e,...r}=t;return(0,n.kt)(b,(0,a.Z)({},l,r,{components:e,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"Substrate, at its origins, was created very close to the conception of the Polkadot network. The two are usually tightly coupled - where the improvements to Substrate funnel into Polkadot and vice versa. Parachains that connect to Polkadot are also built using Substrate, generally using some abstractions such as FRAME and Cumulus."),(0,n.kt)("p",null,"Setting the context is crucial to understand Substrate's role in web3. Polkadot is a relay chain that uses Substrate libraries and FRAME. Substrate is a blockchain developer framework. It enables the creation of solo chains or parachains. Initially, much of what was Substrate came from Polkadot due to necessity. The reasoning behind this choice was if parachains were to be built, it would be sensible to make all standard blockchain protocols and primitives available to parachain developers."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/85386164.04e37665.js b/assets/js/85386164.04e37665.js deleted file mode 100644 index eab83a52c..000000000 --- a/assets/js/85386164.04e37665.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[6561],{3905:(e,t,o)=>{o.d(t,{Zo:()=>d,kt:()=>f});var a=o(7294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function r(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,a)}return o}function i(e){for(var t=1;t=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var l=a.createContext({}),c=function(e){var t=a.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},d=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},h="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var o=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),h=c(o),u=n,f=h["".concat(l,".").concat(u)]||h[u]||p[u]||r;return o?a.createElement(f,i(i({ref:t},d),{},{components:o})):a.createElement(f,i({ref:t},d))}));function f(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=o.length,i=new Array(r);i[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[h]="string"==typeof e?e:n,i[1]=s;for(var c=2;c{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>r,metadata:()=>s,toc:()=>c});var a=o(7462),n=(o(7294),o(3905));const r={id:"npos",sidebar_position:3},i="Nominated Proof of Staking",s={unversionedId:"Polkadot/Module3/npos",id:"Polkadot/Module3/npos",title:"Nominated Proof of Staking",description:"Polkadot's validators are selected by the NPoS scheme. Nominated Proof-of-Stake",source:"@site/docs/Polkadot/Module3/npos.md",sourceDirName:"Polkadot/Module3",slug:"/Polkadot/Module3/npos",permalink:"/docs/Polkadot/Module3/npos",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module3/npos.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1654881714,formattedLastUpdatedAt:"Jun 10, 2022",sidebarPosition:3,frontMatter:{id:"npos",sidebar_position:3},sidebar:"polkadot",previous:{title:"Polkadot Network Consensus",permalink:"/docs/Polkadot/Module3/consensus"},next:{title:"Validators and Nominators",permalink:"/docs/Polkadot/Module3/maintainers"}},l={},c=[{value:"Proof of Stake (PoS) Overview",id:"proof-of-stake-pos-overview",level:2},{value:"Polkadot NPoS Algorithm Complexity",id:"polkadot-npos-algorithm-complexity",level:2},{value:"Proof of Stake (PoS) vs Proof of Work (PoW)",id:"proof-of-stake-pos-vs-proof-of-work-pow",level:2},{value:"Phragmen",id:"phragmen",level:2}],d={toc:c},h="wrapper";function p(e){let{components:t,...o}=e;return(0,n.kt)(h,(0,a.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"nominated-proof-of-staking"},"Nominated Proof of Staking"),(0,n.kt)("p",null,"Polkadot's validators are selected by the NPoS scheme. Nominated Proof-of-Stake\nor NPoS is our adaptation of PoS where an unlimited amount of token holders can participate as\nnominators, backing with their stake a large but limited set of validators. This paradigm simultaneously\nachieves high levels of security and scalability, as well as an unprecedented level of\ndecentralization by ensuring a property known in voting theory as proportional justified representation.\nNominators, who are economically vested in the security of the system, act as watchdogs over the validators'\nperformance. Based on the nominators' expressed preferences over candidates, every era the system selects a\nset of validators with stake backings that are as high. and as evenly distributed as possible. Nominators\nare also economically disincentivized from concentrating their votes on too few validators, which helps\nkeep the system decentralised over time."),(0,n.kt)("p",null,"Furthermore, the election mechanism is highly adaptive to sudden changes, such as some validators\nbeing kicked out after a slashing, as it automatically redistributes the nominators' backings across\nthe new set of validators, even when the votes themselves do not change.\nThe security goal of Polkadot is to be Byzantine fault tolerant when the participants are rational\n(see Section 4.5 for more details on incentives and economics). We assume that with the properties\nNPoS gives, the stakeholders elect a set of validators that has a more than 2/3 fraction of honest\nmembers."),(0,n.kt)("p",null,"The elected validators are responsible for running the relay chain . While each\nparachain's collators are responsible for generating parachain blocks, the validators\nare divided into rotating subsets, one for each parachain, and need to attest to the validity of\nparachain blocks before the headers of those blocks are included in the relay chain.\nTo achieve good scalability the number of validators in each of these subsets is small. Nonethe-\nless, thanks to the NPoS guarantee that every validator is well backed, the availability and validity\nscheme can ensure that any attack on the validity of Polkadot is very expensive in\nexpectation. In fact, the entirety of Polkadot's economic security backs every parachain. This is in\nstark contrast to having, say, 100 independent blockchains with an equivalent sum total of stake,\nwhere on average each blockchain is backed by 1/100-th of the stake, and thus only benefits from\n1/100-th the level of security. We guarantee availability by using erasure coding of each parachain\nblock to make the validators collectively and robustly responsible for the availability of these blocks\nwithout breaking scalability."),(0,n.kt)("h2",{id:"proof-of-stake-pos-overview"},"Proof of Stake (PoS) Overview"),(0,n.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/yKo6pvgbvD8",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,n.kt)("h2",{id:"polkadot-npos-algorithm-complexity"},"Polkadot NPoS Algorithm Complexity"),(0,n.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/HdKEdD_Vdck",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,n.kt)("h2",{id:"proof-of-stake-pos-vs-proof-of-work-pow"},"Proof of Stake (PoS) vs Proof of Work (PoW)"),(0,n.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/6cdzD6lgOXE",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,n.kt)("h2",{id:"phragmen"},"Phragmen"),(0,n.kt)("p",null,"Our decentralisation objective translates into the classical notion of proportional representation in voting theory. That is, a committee should represent each minority in the\nelectorate proportional to their aggregate vote strength (in this case, their stake), with no minority\nbeing under-represented. We highlight here that nominators { and their lists of trusted candidates\n{ constitute a valuable gauge for the preferences of the general community, and that diverse pref-\nerences and factions will naturally arise not only due to economical and security-related reasons,\nbut also political, geographical, etc. Such diversity of points of view is expected and welcome in a\ndecentralised community, and it is important to engage all minorities in decision-making processes\nto ensure user satisfaction."),(0,n.kt)("p",null,"The goal of designing an electoral system that achieves proportional representation has been\npresent in the literature for a very long time. Of special note is the work of Scandinavian math-\nematicians Edvard Phragm\u2000en and Thorvald Thiele in the late nineteenth century. Very recently,\nthere has been considerable e\u2000ort in the research community to formalise the notion of proportional\nrepresentation, and revisit the methods by Phragm\u2000en and Thiele and optimise them algorithmi-\ncally. Our validator selection protocol is an adaptation of Phragm\u2000en's methods and is guaranteed\nto observe the technical property of proportional justified representation (PJR)"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/85386164.3446a23f.js b/assets/js/85386164.3446a23f.js new file mode 100644 index 000000000..c97658e9e --- /dev/null +++ b/assets/js/85386164.3446a23f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[6561],{3905:(e,t,o)=>{o.d(t,{Zo:()=>d,kt:()=>f});var a=o(7294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function r(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,a)}return o}function i(e){for(var t=1;t=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var l=a.createContext({}),c=function(e){var t=a.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},d=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},h="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var o=e.components,n=e.mdxType,r=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),h=c(o),u=n,f=h["".concat(l,".").concat(u)]||h[u]||p[u]||r;return o?a.createElement(f,i(i({ref:t},d),{},{components:o})):a.createElement(f,i({ref:t},d))}));function f(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var r=o.length,i=new Array(r);i[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[h]="string"==typeof e?e:n,i[1]=s;for(var c=2;c{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>r,metadata:()=>s,toc:()=>c});var a=o(7462),n=(o(7294),o(3905));const r={id:"npos",sidebar_position:3},i="Nominated Proof of Staking",s={unversionedId:"Polkadot/Module3/npos",id:"Polkadot/Module3/npos",title:"Nominated Proof of Staking",description:"Polkadot's validators are selected by the NPoS scheme. Nominated Proof-of-Stake",source:"@site/docs/Polkadot/Module3/npos.md",sourceDirName:"Polkadot/Module3",slug:"/Polkadot/Module3/npos",permalink:"/docs/Polkadot/Module3/npos",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module3/npos.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692960998,formattedLastUpdatedAt:"Aug 25, 2023",sidebarPosition:3,frontMatter:{id:"npos",sidebar_position:3},sidebar:"polkadot",previous:{title:"Polkadot Network Consensus",permalink:"/docs/Polkadot/Module3/consensus"},next:{title:"Validators and Nominators",permalink:"/docs/Polkadot/Module3/maintainers"}},l={},c=[{value:"Proof of Stake (PoS) Overview",id:"proof-of-stake-pos-overview",level:2},{value:"Polkadot NPoS Algorithm Complexity",id:"polkadot-npos-algorithm-complexity",level:2},{value:"Proof of Stake (PoS) vs Proof of Work (PoW)",id:"proof-of-stake-pos-vs-proof-of-work-pow",level:2},{value:"Phragmen",id:"phragmen",level:2}],d={toc:c},h="wrapper";function p(e){let{components:t,...o}=e;return(0,n.kt)(h,(0,a.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"nominated-proof-of-staking"},"Nominated Proof of Staking"),(0,n.kt)("p",null,"Polkadot's validators are selected by the NPoS scheme. Nominated Proof-of-Stake\nor NPoS is our adaptation of PoS where an unlimited amount of token holders can participate as\nnominators, backing with their stake a large but limited set of validators. This paradigm simultaneously\nachieves high levels of security and scalability, as well as an unprecedented level of\ndecentralization by ensuring a property known in voting theory as proportional justified representation.\nNominators, who are economically vested in the security of the system, act as watchdogs over the validators'\nperformance. Based on the nominators' expressed preferences over candidates, every era the system selects a\nset of validators with stake backings that are as high. and as evenly distributed as possible. Nominators\nare also economically disincentivized from concentrating their votes on too few validators, which helps\nkeep the system decentralised over time."),(0,n.kt)("p",null,"Furthermore, the election mechanism is highly adaptive to sudden changes, such as some validators\nbeing kicked out after a slashing, as it automatically redistributes the nominators' backings across\nthe new set of validators, even when the votes themselves do not change.\nThe security goal of Polkadot is to be Byzantine fault tolerant when the participants are rational\n(see Section 4.5 for more details on incentives and economics). We assume that with the properties\nNPoS gives, the stakeholders elect a set of validators that has a more than 2/3 fraction of honest\nmembers."),(0,n.kt)("p",null,"The elected validators are responsible for running the relay chain . While each\nparachain's collators are responsible for generating parachain blocks, the validators\nare divided into rotating subsets, one for each parachain, and need to attest to the validity of\nparachain blocks before the headers of those blocks are included in the relay chain.\nTo achieve good scalability the number of validators in each of these subsets is small. Nonethe-\nless, thanks to the NPoS guarantee that every validator is well backed, the availability and validity\nscheme can ensure that any attack on the validity of Polkadot is very expensive in\nexpectation. In fact, the entirety of Polkadot's economic security backs every parachain. This is in\nstark contrast to having, say, 100 independent blockchains with an equivalent sum total of stake,\nwhere on average each blockchain is backed by 1/100-th of the stake, and thus only benefits from\n1/100-th the level of security. We guarantee availability by using erasure coding of each parachain\nblock to make the validators collectively and robustly responsible for the availability of these blocks\nwithout breaking scalability."),(0,n.kt)("h2",{id:"proof-of-stake-pos-overview"},"Proof of Stake (PoS) Overview"),(0,n.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/yKo6pvgbvD8",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,n.kt)("h2",{id:"polkadot-npos-algorithm-complexity"},"Polkadot NPoS Algorithm Complexity"),(0,n.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/HdKEdD_Vdck",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,n.kt)("h2",{id:"proof-of-stake-pos-vs-proof-of-work-pow"},"Proof of Stake (PoS) vs Proof of Work (PoW)"),(0,n.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/6cdzD6lgOXE",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,n.kt)("h2",{id:"phragmen"},"Phragmen"),(0,n.kt)("p",null,"Our decentralisation objective translates into the classical notion of proportional representation in voting theory. That is, a committee should represent each minority in the\nelectorate proportional to their aggregate vote strength (in this case, their stake), with no minority\nbeing under-represented. We highlight here that nominators { and their lists of trusted candidates\n{ constitute a valuable gauge for the preferences of the general community, and that diverse pref-\nerences and factions will naturally arise not only due to economical and security-related reasons,\nbut also political, geographical, etc. Such diversity of points of view is expected and welcome in a\ndecentralised community, and it is important to engage all minorities in decision-making processes\nto ensure user satisfaction."),(0,n.kt)("p",null,"The goal of designing an electoral system that achieves proportional representation has been\npresent in the literature for a very long time. Of special note is the work of Scandinavian math-\nematicians Edvard Phragm\xe9n and Thorvald Thiele in the late nineteenth century. Very recently,\nthere has been considerable effort in the research community to formalise the notion of proportional\nrepresentation, and revisit the methods by Phragm\xe9n and Thiele and optimise them algorithmically. Our validator selection protocol is an adaptation of Phragm\xe9n's methods and is guaranteed\nto observe the technical property of proportional justified representation (PJR)"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8568807f.0034759d.js b/assets/js/8568807f.0034759d.js deleted file mode 100644 index 0b51ef636..000000000 --- a/assets/js/8568807f.0034759d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[9594],{3905:(e,t,o)=>{o.d(t,{Zo:()=>d,kt:()=>m});var n=o(7294);function i(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function a(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function r(e){for(var t=1;t=0||(i[o]=e[o]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(i[o]=e[o])}return i}var l=n.createContext({}),c=function(e){var t=n.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):r(r({},t),e)),o},d=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var o=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),h=c(o),p=i,m=h["".concat(l,".").concat(p)]||h[p]||u[p]||a;return o?n.createElement(m,r(r({ref:t},d),{},{components:o})):n.createElement(m,r({ref:t},d))}));function m(e,t){var o=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=o.length,r=new Array(a);r[0]=p;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[h]="string"==typeof e?e:i,r[1]=s;for(var c=2;c{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var n=o(7462),i=(o(7294),o(3905));const a={id:"consensus"},r="Polkadot Network Consensus",s={unversionedId:"Polkadot/Module3/consensus",id:"Polkadot/Module3/consensus",title:"Polkadot Network Consensus",description:"We need to be able to revert the chain until we know with good probability",source:"@site/docs/Polkadot/Module3/consensus.md",sourceDirName:"Polkadot/Module3",slug:"/Polkadot/Module3/consensus",permalink:"/docs/Polkadot/Module3/consensus",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module3/consensus.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1654881714,formattedLastUpdatedAt:"Jun 10, 2022",frontMatter:{id:"consensus"},sidebar:"polkadot",previous:{title:"Treasury",permalink:"/docs/Polkadot/Module2/treasury"},next:{title:"Nominated Proof of Staking",permalink:"/docs/Polkadot/Module3/npos"}},l={},c=[{value:"BABE",id:"babe",level:2},{value:"Relative Time Protocol",id:"relative-time-protocol",level:3},{value:"GRANDPA Finality Gadget",id:"grandpa-finality-gadget",level:2}],d={toc:c},h="wrapper";function u(e){let{components:t,...o}=e;return(0,i.kt)(h,(0,n.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"polkadot-network-consensus"},"Polkadot Network Consensus"),(0,i.kt)("p",null,"We need to be able to revert the chain until we know with good probability\nthat all parachains are correct. This means that we need to be able to reorganise the chain and\nfor that the chain needs to be capable of forking. Thus we use a block production mechanism,\nBABE, that while run by validators, has similar properties to proof-of-work chains.\nSpeci\u2000cally, we can use the longest chain rule as part of our consensus, and the next block producer\nis not known in advance. On its own BABE would require us to wait a long time from the moment\na block is produced to the moment it is finalised, i.e. when we can be confident that with high\nprobability the block will never be reverted. Slow \u2000finality is required in some circumstances to\ndeal with challenges to availability. Most of the time, however, we would prefer to finalise blocks\nmuch faster. For this purpose, validators \u2000nalise blocks using GRANDPA, a finality\ngadget that is cleanly separated from block production. This separation makes it very adaptive\nand here allows us to delay finalising blocks until challenges are dealt with, without slowing down\nblock production. GRANDPA gets Byzantine agreement on finalised blocks and will allow us to\nprove to an entity that keeps track of the validator set which blocks are finalised, which will be\nimportant for bridges."),(0,i.kt)("h2",{id:"babe"},"BABE"),(0,i.kt)("p",null,'In Polkadot, we produce relay chain blocks using our Blind Assignment for Blockchain Extension\nprotocol (BABE). BABE assigns validators randomly to block production slots using the random-\nness generated with blocks. A block production slot is a division of time when a block producer may\nproduce a block. Note, that time is not universally agreed on, which we will address later. These\nassignments are completely private until the assigned validators produce their blocks. Therefore,\nwe use \\Blind Assignment" in the protocol name. BABE is similar to Ouroboros Praos with\nsome significant differences in the chain selection rule and timing assumptions.\nIn BABE, we may have slots without any assignment which we call empty slot. In order to \u2000ll\nthe empty slots, we have a secondary block production mechanism based on Aura that assigns\nvalidators to slots publicly. We note that these blocks do not contribute to the security of BABE\nsince the best chain selection and the random number generation algorithms work as if Aura blocks\ndo not exist. Therefore, next we only describe BABE together with its security properties.'),(0,i.kt)("h3",{id:"relative-time-protocol"},"Relative Time Protocol"),(0,i.kt)("p",null,"Relative Time Protocol: The elected validators for a slot need to know when the right time\nis to produce a block for the consistency and the security of BABE. For this, validators uses their\nlocal computer clock which is not adjusted by any centralized clock adjustment protocols such\nas the Network Time Protocol. Instead, they keep their clock synchronised with the other\nvalidators with the relative time protocol. "),(0,i.kt)("p",null,"In BABE, we assume that after the genesis block is released, elected validators of the \u2000rst epoch\nstore the arrival time of the genesis block with respect to their local clock. Then, they mark the\nbeginning time of the \u2000rst slot and increment the slot number every T seconds. After this point,\nthey periodically run the relative algorithm not to lose the synchronisation with others because of\ntheir local clock drifts. In addition to this, a validator who joins after the genesis block runs the\nrelative time algorithm to be synchronised with the other validators."),(0,i.kt)("p",null,"As mentioned above, we want a \u2000nalisation mechanism that is\nexible and separated from block\nproduction, which is achieved by GRANDPA. The only modi\u2000cation to BABE required for it to\nwork with GRANDPA is to change the fork-choice rule: instead of building on the longest chain,\na validator producing a block should build on the longest chain including all blocks that it sees as\n\u2000nalised. GRANDPA can work with many di\u2000erent block production mechanisms and it will be\npossible to switch out BABE with another.\nIntuitively GRANDPA is a Byzantine agreement protocol that works to agree on a chain,\nout of many possible forks, by following some simpler fork choice rule, which together with the\nblock production mechanism would give probabilistic \u2000nality if GRANDPA itself stopped \u2000nalising\nblocks. We want to be able to agree on many blocks at once, in contrast to single-block Byzantine\nagreement protocols.\nWe assume that we can ask the fork choice rule for the best block given a particular block. The\nbasic idea is that we want to reach Byzantine agreement on the pre\u2000x of the chain that everyone\nagrees on. To make this more robust, we try to agree on the pre\u2000x of the chain that 2/3 of\nvalidators agree on."),(0,i.kt)("h2",{id:"grandpa-finality-gadget"},"GRANDPA Finality Gadget"),(0,i.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/0shPS6SXPKE",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8568807f.bf6000b1.js b/assets/js/8568807f.bf6000b1.js new file mode 100644 index 000000000..f1c208af9 --- /dev/null +++ b/assets/js/8568807f.bf6000b1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[9594],{3905:(e,t,o)=>{o.d(t,{Zo:()=>d,kt:()=>f});var n=o(7294);function i(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function a(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function r(e){for(var t=1;t=0||(i[o]=e[o]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(i[o]=e[o])}return i}var l=n.createContext({}),c=function(e){var t=n.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):r(r({},t),e)),o},d=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var o=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),h=c(o),p=i,f=h["".concat(l,".").concat(p)]||h[p]||u[p]||a;return o?n.createElement(f,r(r({ref:t},d),{},{components:o})):n.createElement(f,r({ref:t},d))}));function f(e,t){var o=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=o.length,r=new Array(a);r[0]=p;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[h]="string"==typeof e?e:i,r[1]=s;for(var c=2;c{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var n=o(7462),i=(o(7294),o(3905));const a={id:"consensus"},r="Polkadot Network Consensus",s={unversionedId:"Polkadot/Module3/consensus",id:"Polkadot/Module3/consensus",title:"Polkadot Network Consensus",description:"We need to be able to revert the chain until we know with good probability",source:"@site/docs/Polkadot/Module3/consensus.md",sourceDirName:"Polkadot/Module3",slug:"/Polkadot/Module3/consensus",permalink:"/docs/Polkadot/Module3/consensus",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module3/consensus.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"consensus"},sidebar:"polkadot",previous:{title:"Treasury",permalink:"/docs/Polkadot/Module2/treasury"},next:{title:"Nominated Proof of Staking",permalink:"/docs/Polkadot/Module3/npos"}},l={},c=[{value:"BABE",id:"babe",level:2},{value:"Relative Time Protocol",id:"relative-time-protocol",level:3},{value:"GRANDPA Finality Gadget",id:"grandpa-finality-gadget",level:2}],d={toc:c},h="wrapper";function u(e){let{components:t,...o}=e;return(0,i.kt)(h,(0,n.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"polkadot-network-consensus"},"Polkadot Network Consensus"),(0,i.kt)("p",null,"We need to be able to revert the chain until we know with good probability\nthat all parachains are correct. This means that we need to be able to reorganise the chain and\nfor that the chain needs to be capable of forking. Thus we use a block production mechanism,\nBABE, that while run by validators, has similar properties to proof-of-work chains.\nSpecifically, we can use the longest chain rule as part of our consensus, and the next block producer\nis not known in advance. On its own BABE would require us to wait a long time from the moment\na block is produced to the moment it is finalised, i.e. when we can be confident that with high\nprobability the block will never be reverted. Slow finality is required in some circumstances to\ndeal with challenges to availability. Most of the time, however, we would prefer to finalise blocks\nmuch faster. For this purpose, validators finalise blocks using GRANDPA, a finality\ngadget that is cleanly separated from block production. This separation makes it very adaptive\nand here allows us to delay finalising blocks until challenges are dealt with, without slowing down\nblock production. GRANDPA gets Byzantine agreement on finalised blocks and will allow us to\nprove to an entity that keeps track of the validator set which blocks are finalised, which will be\nimportant for bridges."),(0,i.kt)("h2",{id:"babe"},"BABE"),(0,i.kt)("p",null,'In Polkadot, we produce relay chain blocks using our Blind Assignment for Blockchain Extension\nprotocol (BABE). BABE assigns validators randomly to block production slots using the random-\nness generated with blocks. A block production slot is a division of time when a block producer may\nproduce a block. Note, that time is not universally agreed on, which we will address later. These\nassignments are completely private until the assigned validators produce their blocks. Therefore,\nwe use \\Blind Assignment" in the protocol name. BABE is similar to Ouroboros Praos with\nsome significant differences in the chain selection rule and timing assumptions.\nIn BABE, we may have slots without any assignment which we call empty slot. In order to fill\nthe empty slots, we have a secondary block production mechanism based on Aura that assigns\nvalidators to slots publicly. We note that these blocks do not contribute to the security of BABE\nsince the best chain selection and the random number generation algorithms work as if Aura blocks\ndo not exist. Therefore, next we only describe BABE together with its security properties.'),(0,i.kt)("h3",{id:"relative-time-protocol"},"Relative Time Protocol"),(0,i.kt)("p",null,"Relative Time Protocol: The elected validators for a slot need to know when the right time\nis to produce a block for the consistency and the security of BABE. For this, validators uses their\nlocal computer clock which is not adjusted by any centralized clock adjustment protocols such\nas the Network Time Protocol. Instead, they keep their clock synchronised with the other\nvalidators with the relative time protocol."),(0,i.kt)("p",null,"In BABE, we assume that after the genesis block is released, elected validators of the first epoch\nstore the arrival time of the genesis block with respect to their local clock. Then, they mark the\nbeginning time of the first slot and increment the slot number every T seconds. After this point,\nthey periodically run the relative algorithm not to lose the synchronisation with others because of\ntheir local clock drifts. In addition to this, a validator who joins after the genesis block runs the\nrelative time algorithm to be synchronised with the other validators."),(0,i.kt)("p",null,"As mentioned above, we want a finalisation mechanism that is\nexible and separated from block\nproduction, which is achieved by GRANDPA. The only modification to BABE required for it to\nwork with GRANDPA is to change the fork-choice rule: instead of building on the longest chain,\na validator producing a block should build on the longest chain including all blocks that it sees as\nfinalised. GRANDPA can work with many different block production mechanisms and it will be\npossible to switch out BABE with another.\nIntuitively GRANDPA is a Byzantine agreement protocol that works to agree on a chain,\nout of many possible forks, by following some simpler fork choice rule, which together with the\nblock production mechanism would give probabilistic finality if GRANDPA itself stopped finalising\nblocks. We want to be able to agree on many blocks at once, in contrast to single-block Byzantine\nagreement protocols.\nWe assume that we can ask the fork choice rule for the best block given a particular block. The\nbasic idea is that we want to reach Byzantine agreement on the prefix of the chain that everyone\nagrees on. To make this more robust, we try to agree on the prefix of the chain that 2/3 of\nvalidators agree on."),(0,i.kt)("h2",{id:"grandpa-finality-gadget"},"GRANDPA Finality Gadget"),(0,i.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/0shPS6SXPKE",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8a7c7b85.2ea69f52.js b/assets/js/8a7c7b85.2ea69f52.js new file mode 100644 index 000000000..bebf8f6de --- /dev/null +++ b/assets/js/8a7c7b85.2ea69f52.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[4937],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},y=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,s=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),p=l(n),y=o,h=p["".concat(c,".").concat(y)]||p[y]||d[y]||s;return n?r.createElement(h,a(a({ref:t},u),{},{components:n})):r.createElement(h,a({ref:t},u))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var s=n.length,a=new Array(s);a[0]=y;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i[p]="string"==typeof e?e:o,a[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>d,frontMatter:()=>s,metadata:()=>i,toc:()=>l});var r=n(7462),o=(n(7294),n(3905));const s={id:"cryptography"},a="Cryptography",i={unversionedId:"Polkadot/Module4/cryptography",id:"Polkadot/Module4/cryptography",title:"Cryptography",description:"We assume that malicious parties generate their keys with an arbitrary algorithm while",source:"@site/docs/Polkadot/Module4/cryptography.md",sourceDirName:"Polkadot/Module4",slug:"/Polkadot/Module4/cryptography",permalink:"/docs/Polkadot/Module4/cryptography",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module4/cryptography.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"cryptography"},sidebar:"polkadot",previous:{title:"Security and Consensus Improvements",permalink:"/docs/Polkadot/Module3/securityimprovements"},next:{title:"Networking",permalink:"/docs/Polkadot/Module4/networking"}},c={},l=[{value:"Account Keys",id:"account-keys",level:2},{value:"Session Keys",id:"session-keys",level:2}],u={toc:l},p="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"cryptography"},"Cryptography"),(0,o.kt)("p",null,"We assume that malicious parties generate their keys with an arbitrary algorithm while\nhonest ones always generate their keys securely."),(0,o.kt)("p",null,"In Polkadot, we necessarily distinguish among different permissions and functionalities with dif-\nferent keys and key types, respectively. We roughly categorise these into account keys with which\nusers interact and session keys that nodes manage without operator intervention beyond a certificcation process."),(0,o.kt)("h2",{id:"account-keys"},"Account Keys"),(0,o.kt)("p",null,"Account keys have an associated balance of which portions can be locked to play roles in staking,\nresource rental, and governance, including waiting out a couple types of unlocking period. We\nallow several locks of varying duration, both because these roles impose different restrictions, and\nfor multiple unlocking periods running concurrently.\nWe encourage active participation in all these roles, but they all require occasional signatures\nfrom accounts. At the same time, account keys have better physical security when kept in incon-\nvenient locations, like safety deposit boxes, which makes signing arduous. We avoid this friction\nfor users as follows.\nAccounts that lock funds for staking are called stash accounts. All stash accounts register a\ncertificate on-chain that delegates all validator operation and nomination powers to some controller\naccount, and also designates some proxy key for governance votes. In this state, the controller and\nproxy accounts can sign for the stash account in staking and governance functions respectively,\nbut not transfer funds.\nAt present, we support both ed25519 and Schnorrkel/sr25519 for account keys. These\nare both Schnorr-like signatures implemented using the Ed25519 curve, so both offer extremely\nsimilar security. We recommend ed25519 keys for users who require Hardware Security Module\n(HSM) support or other external key management solution, while Schnorrkel/sr25519 provides\nmore blockchain-friendly functionality like Hierarchical Deterministic Key Derivation (HDKD)\nand multi-signatures.\nIn particular, Schnorrkel/sr25519 uses the Ristretto implementation of Mike Hamburg's\nDecaf, which provide the 2-torsion free points of the Ed25519 curve as a prime order\ngroup. Avoiding the cofactor like this means Ristretto makes implementing more complex pro-\ntocols significantly safer. We employ Blake2b for most conventional hashing in Polkadot, but\nSchnorrkel/sr25519 itself uses STROBE128, which is based on Keccak-f(1600) and provides a\nhashing interface well suited to signatures and non-interactive zero-knowledge proofs (NIZKs)."),(0,o.kt)("h2",{id:"session-keys"},"Session Keys"),(0,o.kt)("p",null,"Session keys each fill roughly one particular role in consensus or security. As a rule, session keys gain\nauthority only from a session certificate, signed by some controller key, that delegates appropriate\nstake.\nAt any time, the controller key can pause or revoke this session certificate and/or issue re-\nplacement with new session keys. All new session keys can be registered in advance, and most\nmust be, so validators can cleanly transition to new hardware by issuing session certificates that\nonly become valid after some future session. We suggest using pause mechanism for emergency\nmaintenance and using revocation if a session key might be compromised.\nWe prefer if session keys remain tied to one physical machine because doing so minimises the\nrisk of accidental equivocation. We ask validator operators to issue session certificates using an\nRPC protocol, not to handle the session secret keys themselves.\nAlmost all early proof-of-stake networks have a negligent public key infrastructure that en-\ncourages duplicating session secret keys across machines, and thus reduces security and leads to\npointless slashing.\nWe impose no prior restrictions on the cryptography employed by specific components or their\nassociated session keys types"))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8a7c7b85.77aabbde.js b/assets/js/8a7c7b85.77aabbde.js deleted file mode 100644 index fb7829336..000000000 --- a/assets/js/8a7c7b85.77aabbde.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[4937],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=o.createContext({}),l=function(e){var t=o.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=l(e.components);return o.createElement(c.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},y=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,s=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),p=l(n),y=r,h=p["".concat(c,".").concat(y)]||p[y]||d[y]||s;return n?o.createElement(h,a(a({ref:t},u),{},{components:n})):o.createElement(h,a({ref:t},u))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var s=n.length,a=new Array(s);a[0]=y;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i[p]="string"==typeof e?e:r,a[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>d,frontMatter:()=>s,metadata:()=>i,toc:()=>l});var o=n(7462),r=(n(7294),n(3905));const s={id:"cryptography"},a="Cryptography",i={unversionedId:"Polkadot/Module4/cryptography",id:"Polkadot/Module4/cryptography",title:"Cryptography",description:"We assume that malicious parties generate their keys with an arbitrary algorithm while",source:"@site/docs/Polkadot/Module4/cryptography.md",sourceDirName:"Polkadot/Module4",slug:"/Polkadot/Module4/cryptography",permalink:"/docs/Polkadot/Module4/cryptography",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module4/cryptography.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1654881714,formattedLastUpdatedAt:"Jun 10, 2022",frontMatter:{id:"cryptography"},sidebar:"polkadot",previous:{title:"Security and Consensus Improvements",permalink:"/docs/Polkadot/Module3/securityimprovements"},next:{title:"Networking",permalink:"/docs/Polkadot/Module4/networking"}},c={},l=[{value:"Account Keys",id:"account-keys",level:2},{value:"Session Keys",id:"session-keys",level:2}],u={toc:l},p="wrapper";function d(e){let{components:t,...n}=e;return(0,r.kt)(p,(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"cryptography"},"Cryptography"),(0,r.kt)("p",null,"We assume that malicious parties generate their keys with an arbitrary algorithm while\nhonest ones always generate their keys securely."),(0,r.kt)("p",null,"In Polkadot, we necessarily distinguish among di\u2000erent permissions and functionalities with dif-\nferent keys and key types, respectively. We roughly categorise these into account keys with which\nusers interact and session keys that nodes manage without operator intervention beyond a certificcation process."),(0,r.kt)("h2",{id:"account-keys"},"Account Keys"),(0,r.kt)("p",null,"Account keys have an associated balance of which portions can be locked to play roles in staking,\nresource rental, and governance, including waiting out a couple types of unlocking period. We\nallow several locks of varying duration, both because these roles impose di\u2000erent restrictions, and\nfor multiple unlocking periods running concurrently.\nWe encourage active participation in all these roles, but they all require occasional signatures\nfrom accounts. At the same time, account keys have better physical security when kept in incon-\nvenient locations, like safety deposit boxes, which makes signing arduous. We avoid this friction\nfor users as follows.\nAccounts that lock funds for staking are called stash accounts. All stash accounts register a\ncerti\u2000cate on-chain that delegates all validator operation and nomination powers to some controller\naccount, and also designates some proxy key for governance votes. In this state, the controller and\nproxy accounts can sign for the stash account in staking and governance functions respectively,\nbut not transfer funds.\nAt present, we support both ed25519 and Schnorrkel/sr25519 for account keys. These\nare both Schnorr-like signatures implemented using the Ed25519 curve, so both o\u2000er extremely\nsimilar security. We recommend ed25519 keys for users who require Hardware Security Module\n(HSM) support or other external key management solution, while Schnorrkel/sr25519 provides\nmore blockchain-friendly functionality like Hierarchical Deterministic Key Derivation (HDKD)\nand multi-signatures.\nIn particular, Schnorrkel/sr25519 uses the Ristretto implementation of Mike Hamburg's\nDecaf, which provide the 2-torsion free points of the Ed25519 curve as a prime order\ngroup. Avoiding the cofactor like this means Ristretto makes implementing more complex pro-\ntocols signi\u2000cantly safer. We employ Blake2b for most conventional hashing in Polkadot, but\nSchnorrkel/sr25519 itself uses STROBE128, which is based on Keccak-f(1600) and provides a\nhashing interface well suited to signatures and non-interactive zero-knowledge proofs (NIZKs)."),(0,r.kt)("h2",{id:"session-keys"},"Session Keys"),(0,r.kt)("p",null,"Session keys each fill roughly one particular role in consensus or security. As a rule, session keys gain\nauthority only from a session certi\u2000cate, signed by some controller key, that delegates appropriate\nstake.\nAt any time, the controller key can pause or revoke this session certi\u2000cate and/or issue re-\nplacement with new session keys. All new session keys can be registered in advance, and most\nmust be, so validators can cleanly transition to new hardware by issuing session certi\u2000cates that\nonly become valid after some future session. We suggest using pause mechanism for emergency\nmaintenance and using revocation if a session key might be compromised.\nWe prefer if session keys remain tied to one physical machine because doing so minimises the\nrisk of accidental equivocation. We ask validator operators to issue session certi\u2000cates using an\nRPC protocol, not to handle the session secret keys themselves.\nAlmost all early proof-of-stake networks have a negligent public key infrastructure that en-\ncourages duplicating session secret keys across machines, and thus reduces security and leads to\npointless slashing.\nWe impose no prior restrictions on the cryptography employed by specific components or their\nassociated session keys types"))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/9eca237c.7a60832e.js b/assets/js/9eca237c.eece0504.js similarity index 50% rename from assets/js/9eca237c.7a60832e.js rename to assets/js/9eca237c.eece0504.js index 2ed68e49b..6b71d9d7a 100644 --- a/assets/js/9eca237c.7a60832e.js +++ b/assets/js/9eca237c.eece0504.js @@ -1 +1 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[3393],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>m});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),d=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=d(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=d(n),f=o,m=u["".concat(c,".").concat(f)]||u[f]||p[f]||a;return n?r.createElement(m,i(i({ref:t},s),{},{components:n})):r.createElement(m,i({ref:t},s))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=f;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:o,i[1]=l;for(var d=2;d{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>d});var r=n(7462),o=(n(7294),n(3905));const a={id:"decentralization"},i="Decentralization of Network",l={unversionedId:"Polkadot/Module4/decentralization",id:"Polkadot/Module4/decentralization",title:"Decentralization of Network",description:"Of course, in a real-world decentralised system the networking part also must be decentralised -",source:"@site/docs/Polkadot/Module4/decentralization.md",sourceDirName:"Polkadot/Module4",slug:"/Polkadot/Module4/decentralization",permalink:"/docs/Polkadot/Module4/decentralization",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module4/decentralization.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1656502743,formattedLastUpdatedAt:"Jun 29, 2022",frontMatter:{id:"decentralization"},sidebar:"polkadot",previous:{title:"Path of a Parachain Block",permalink:"/docs/Polkadot/Module4/parachainblock"},next:{title:"Interoperability",permalink:"/docs/Polkadot/Module5/interoperability"}},c={},d=[{value:"Why Decentralize?",id:"why-decentralize",level:2},{value:"Polkadot Decentralization Efforts",id:"polkadot-decentralization-efforts",level:2}],s={toc:d},u="wrapper";function p(e){let{components:t,...n}=e;return(0,o.kt)(u,(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"decentralization-of-network"},"Decentralization of Network"),(0,o.kt)("p",null,"Of course, in a real-world decentralised system the networking part also must be decentralised -\nit's no good if all communication passes through a few central servers, even if the high-level protocol\nrunning on top of it is decentralised with respect to its entities. As a concrete example: in certain\nsecurity models, including the traditional Byzantine fault-tolerant setting, nodes are modelled as\npossibly malicious but no consideration is given to malicious edges. A security requirement like"),(0,o.kt)("blockquote",null,(0,o.kt)("p",{parentName:"blockquote"},"1=3 of nodes are honest in the model, in fact translates to > 1=3 of nodes are honest and can\nall communicate perfectly reliably with each other all the time in reality. Conversely, if an edge\nis controlled by a malicious ISP in reality, it is the corresponding node(s) that must be treated as\nmalicious in any analysis under the model. More signi\u2000cantly, if the underlying communications\nnetwork is centralised, this can give the central parties the ability to corrupt > 1=3 of nodes within\nthe model thereby breaking its security assumptions, even if they don't actually have arbitrary\nexecution rights on that many nodes.\nIn this section we outline and enumerate the communication primitives that we require in\nPolkadot, and sketch a high-level design on how we achieve these in a decentralised way, with the\nspecifics to be re\u2000ned as we move forward with a production system.")),(0,o.kt)("h2",{id:"why-decentralize"},"Why Decentralize?"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/-xOK970mS14",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,o.kt)("h2",{id:"polkadot-decentralization-efforts"},"Polkadot Decentralization Efforts"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/Zub9TCWQbf8",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[3393],{3905:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>m});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),d=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=d(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=d(n),f=o,m=u["".concat(c,".").concat(f)]||u[f]||p[f]||a;return n?r.createElement(m,i(i({ref:t},s),{},{components:n})):r.createElement(m,i({ref:t},s))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=f;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:o,i[1]=l;for(var d=2;d{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>d});var r=n(7462),o=(n(7294),n(3905));const a={id:"decentralization"},i="Decentralization of Network",l={unversionedId:"Polkadot/Module4/decentralization",id:"Polkadot/Module4/decentralization",title:"Decentralization of Network",description:"Of course, in a real-world decentralised system the networking part also must be decentralised -",source:"@site/docs/Polkadot/Module4/decentralization.md",sourceDirName:"Polkadot/Module4",slug:"/Polkadot/Module4/decentralization",permalink:"/docs/Polkadot/Module4/decentralization",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module4/decentralization.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"decentralization"},sidebar:"polkadot",previous:{title:"Path of a Parachain Block",permalink:"/docs/Polkadot/Module4/parachainblock"},next:{title:"Interoperability",permalink:"/docs/Polkadot/Module5/interoperability"}},c={},d=[{value:"Why Decentralize?",id:"why-decentralize",level:2},{value:"Polkadot Decentralization Efforts",id:"polkadot-decentralization-efforts",level:2}],s={toc:d},u="wrapper";function p(e){let{components:t,...n}=e;return(0,o.kt)(u,(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"decentralization-of-network"},"Decentralization of Network"),(0,o.kt)("p",null,"Of course, in a real-world decentralised system the networking part also must be decentralised -\nit's no good if all communication passes through a few central servers, even if the high-level protocol\nrunning on top of it is decentralised with respect to its entities. As a concrete example: in certain\nsecurity models, including the traditional Byzantine fault-tolerant setting, nodes are modelled as\npossibly malicious but no consideration is given to malicious edges. A security requirement like"),(0,o.kt)("blockquote",null,(0,o.kt)("p",{parentName:"blockquote"},"1=3 of nodes are honest in the model, in fact translates to > 1=3 of nodes are honest and can\nall communicate perfectly reliably with each other all the time in reality. Conversely, if an edge\nis controlled by a malicious ISP in reality, it is the corresponding node(s) that must be treated as\nmalicious in any analysis under the model. More significantly, if the underlying communications\nnetwork is centralised, this can give the central parties the ability to corrupt > 1=3 of nodes within\nthe model thereby breaking its security assumptions, even if they don't actually have arbitrary\nexecution rights on that many nodes.\nIn this section we outline and enumerate the communication primitives that we require in\nPolkadot, and sketch a high-level design on how we achieve these in a decentralised way, with the\nspecifics to be refined as we move forward with a production system.")),(0,o.kt)("h2",{id:"why-decentralize"},"Why Decentralize?"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/-xOK970mS14",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}),(0,o.kt)("h2",{id:"polkadot-decentralization-efforts"},"Polkadot Decentralization Efforts"),(0,o.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/Zub9TCWQbf8",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ddd8c84d.6b6021cc.js b/assets/js/ddd8c84d.6b6021cc.js new file mode 100644 index 000000000..d64a1b707 --- /dev/null +++ b/assets/js/ddd8c84d.6b6021cc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[8845],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},m="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=u(n),h=o,d=m["".concat(s,".").concat(h)]||m[h]||c[h]||i;return n?a.createElement(d,r(r({ref:t},p),{},{components:n})):a.createElement(d,r({ref:t},p))}));function d(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,r=new Array(i);r[0]=h;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:o,r[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>r,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var a=n(7462),o=(n(7294),n(3905));const i={id:"module3",sidebar_position:1},r="3. Intro to Intermediate Rust",l={unversionedId:"Rust/section3/module3",id:"Rust/section3/module3",title:"3. Intro to Intermediate Rust",description:"Structs. Enums. Methods. Packages and Crates.",source:"@site/docs/Rust/section3/module3.md",sourceDirName:"Rust/section3",slug:"/Rust/section3/module3",permalink:"/docs/Rust/section3/module3",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Rust/section3/module3.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692960998,formattedLastUpdatedAt:"Aug 25, 2023",sidebarPosition:1,frontMatter:{id:"module3",sidebar_position:1}},s={},u=[{value:"API Methods",id:"api-methods",level:2},{value:"Associated Functions",id:"associated-functions",level:2},{value:"Area Example",id:"area-example",level:2},{value:"Trait Bounds for Function parameters",id:"trait-bounds-for-function-parameters",level:2},{value:"Conditional Method Implementation",id:"conditional-method-implementation",level:2},{value:"Implementing Multiply for our Fraction type",id:"implementing-multiply-for-our-fraction-type",level:2},{value:"Limitation",id:"limitation",level:2},{value:"Types",id:"types",level:2},{value:"Traits",id:"traits-1",level:2}],p={toc:u},m="wrapper";function c(e){let{components:t,...n}=e;return(0,o.kt)(m,(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"3-intro-to-intermediate-rust"},"3. Intro to Intermediate Rust"),(0,o.kt)("p",null,"Structs. Enums. Methods. Packages and Crates.\nCargo feature - this will be important for the \u201cstd\u201d features all over Substrate. \u201cruntime-benchmarks\u201d and \u201ctry-runtime\u201d."),(0,o.kt)("p",null,"In this module we will begin to explore Rust's type system. We will create our own types and add methods to those types. We will also take our first look at Generics and Traits in Rust. There will be much more to cover about the type system later in the course, but in this module we will learn the most fundamental and common aspects."),(0,o.kt)("p",null,'Rust allows users to create two different broad categories of types. First, there are Structs which are sometimes known as "product types" or "each-of types" because a struct instance requires data in each of its fields. Second, there are Enums which are sometimes known as "sum types" or "one-of types" because an enum instance is one of multiple variants.'),(0,o.kt)("p",null,"If you come from an object oriented programming background, structs will be familiar as they are pretty similar to the data fields on a class or object. If you come from an functional programming background, both structs and enums will likely be familiar, although the enum syntax is unlike that of OCaml or Haskell."),(0,o.kt)("h1",{id:"structs"},"Structs"),(0,o.kt)("p",null,"Structs allow packaging multiple primitive data types together into a logical package. They are very similar to the tuples we saw previously, although they make the language much more programmer friendly."),(0,o.kt)("p",null,"When defining a struct, you provide a name for the type, which has a capital letter by convention. Then you provide a name for each field and a type for each field."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Fraction {\n numerator: u32,\n denominator: u32,\n}\n")),(0,o.kt)("p",null,"To create an instance of a struct, you use the name of the type and the name of each field as before. But instead of specifying a type for each field, which has already been defined, we supply a value for each field."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let one_half = Fraction {\n numerator: 1,\n denominator: 2,\n}\n")),(0,o.kt)("p",null,"Structs, like tuples, need to have a value for each and every field. It is not valid to omit one of the fields."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let one = Fraction {\n numerator: 1,\n}\n")),(0,o.kt)("p",null,"Similarly, each field must be of the type that was declared when the struct was defined."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let one_half = Fraction {\n numerator: 1u64,\n denominator: 2u64,\n}\n")),(0,o.kt)("p",null,"Now that we know how declare and instantiate structs, let's see how to access their fields. To access the inner data of a struct, we just use a dot operator and then the name of the field."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let my_frac = Fraction {\n numerator: 2,\n denominator: 3,\n}\n\nlet my_bottom_value = my_frac.denominator;\nassert!(my_bottom_value == 3);\n")),(0,o.kt)("p",null,"Notice that here we have used simple integer literals like ",(0,o.kt)("inlineCode",{parentName:"p"},"3")," as opposed to explicitly typed ones like ",(0,o.kt)("inlineCode",{parentName:"p"},"3u32"),". This is allowed because the Rust compiler knows that the fields of the ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction")," type are ",(0,o.kt)("inlineCode",{parentName:"p"},"u32")," because they were declared as such. Similarly, the compiler can infer that the type of ",(0,o.kt)("inlineCode",{parentName:"p"},"my_bottom_value")," is u32 so we don't need an explicit annotation."),(0,o.kt)("p",null,"We can use the same syntax to mutate the value of a field. But, as always in Rust, to make a value mutable it must be explicitly declared as such."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let mut f1 = Fraction {\n numerator: 1,\n denominator: 2,\n};\n\nlet f2 = Fraction {\n numerator: 1,\n denominator: 2,\n};\n\n// here we can mutate the denominator of f1\nf1.denominator = 3;\n\n// but this line will not compile because f2 is immutable\nf2.denominator = 4;\n")),(0,o.kt)("h1",{id:"tuple-structs"},"Tuple Structs"),(0,o.kt)("p",null,"In some cases you want to give your type a name like you can with structs, but you don't want to give each field a name. One such example would be storing a point on a 2D lattice."),(0,o.kt)("p",null,"So far we've learned two ways to store this data, both of which work, but neither of which are exactly what we want. We could simply use a tuple, but this does not allow us\nto name the type, and could lead to confusion and even bugs if tuples are used in another context in the program.\nOr we could use a struct, but this require repeating field names often when the position is a well-established mathematical convention."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"// We could simply use a tuple.\nlet point = (2u32, 3u32);\n\n// We could use a struct\nstruct Point {\n x_coord: u32,\n y_coord: u32,\n}\n")),(0,o.kt)("p",null,"For cases like this, Rust has the tuple struct. This allows us to name the type while referencing the fields positionally."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Point(u32, u32);\n")),(0,o.kt)("p",null,"When using tuple structs, the fields are accessed with the dot operator and an integer index exactly like they are when working with tuples."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let buried_treasure = Point(4, 5);\n\nlet treasure_x_coord = buried_treasure.0;\n")),(0,o.kt)("p",null,'It is also possible to create tuple structs with a single element inside. You may wonder why you would ever want to do this when you could simply use the underlying type directly, but in fact this is done frequently, and is often referred to as the "new type pattern". The new type pattern allows us the leverage Rust\'s type system to distinguish values of different semantic units even if they are represented digitally by the same underlying primitive type.'),(0,o.kt)("p",null,"A typical example would be something like storing Temperature data when you want to be sure that the units are in Celsius as opposed to something like Kelvin or Fahrenheit."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// A temperature value that is stored in the celsius units.\nstruct Celsius(u32);\n")),(0,o.kt)("p",null,"As a side note, notice the comment starting with a triple slash here. This is known as a doc comment or a documentation comment. Doc comments can precede data types, functions, and many other pieces of code and be used to generate automated documentation. We will not explore this in depth here, but know that it is good practice to use doc comments when creating your own types."),(0,o.kt)("p",null,"Finally, it is possible to create unit structs that have no fields at all. They are analogous to Rust's built-in unit type ",(0,o.kt)("inlineCode",{parentName:"p"},"()"),". This is useful when you want a type to declare static methods on. We will cover methods later in this module."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// A unit type that has no data fields.\nstruct MyUnitType;\n")),(0,o.kt)("h1",{id:"custom-types-in-functions-and-other-types"},"Custom types in Functions and Other Types"),(0,o.kt)("p",null,"Now that we've created some custom types of our own, let's see some ways that they can be used."),(0,o.kt)("p",null,"One way we can use our custom types is as parameters to functions. Let's define a function that allows us to multiply two fractions."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"fn multiply_fractions(a: Fraction, b: Fraction) -> Fraction {\n Fraction {\n numerator: a.numerator * b.numerator,\n denominator: a.denominator * b.denominator,\n }\n}\n")),(0,o.kt)("p",null,"Now we are ready to see the value of the new type pattern we discussed previously. Consider steam engine control circuit with a function that determines whether the boiler has warmed up enough to start the engine."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// First attempt that is subject to unit confusion\nfn warm_enough(temp: u32) -> bool {\n temp > 100\n}\n")),(0,o.kt)("p",null,"In this naive function we run the risk of a user entering a Fahrenheit temperature by mistake, or interfacing with a temperature probe that is incorrectly configured to report Fahrenheit temperature."),(0,o.kt)("p",null,"We can make this function safer by using Rust's type system to insist that a Celsius temperate is entered. This program will not even compile if a plain u32 is passed."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Celsius(u32);\n\nfn warm_enough(temp: Celsius) -> bool {\n temp.0 > 100\n}\n")),(0,o.kt)("p",null,"Another place we can use our custom types is as fields of other custom types. Consider a geometry program that builds up shapes from more fundamental primitive types."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Point(u32, u32);\n\n/// We define a Rectangle by its two opposite corners\nstruct Rectangle {\n top_left: Point,\n bottom_right: Point,\n}\n")),(0,o.kt)("h1",{id:"enums"},"Enums"),(0,o.kt)("p",null,"TODO"),(0,o.kt)("h1",{id:"methods"},"Methods"),(0,o.kt)("p",null,"We saw previously how we can write functions that take our custom data types as parameters or return our custom data types. Rust also supports the notion of a method. A method is a special function that is defined in the context of a particular type."),(0,o.kt)("p",null,"If you are familiar with Object Oriented languages like Java, methods will be very familiar. In Rust, methods are defined in a separate block than the type itself, unlike Java where the methods and data are all part of the same class. This adds some flexibility to Rust as we will see. Methods always take function"),(0,o.kt)("p",null,"Methods are very similar to functions in syntax. The only difference is that they go in an ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block. As an illustrative example, lets re-write the ",(0,o.kt)("inlineCode",{parentName:"p"},"multiply_fraction")," function the we looked at earlier as a method."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(self, other: Fraction) -> Fraction {\n Fraction {\n numerator: self.numerator * other.numerator,\n denominator: self.denominator * other.denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"This method is very similar to the function we wrote previously. The main difference is the first parameter, ",(0,o.kt)("inlineCode",{parentName:"p"},"self"),". Notice that this ",(0,o.kt)("inlineCode",{parentName:"p"},"self")," has a lowercase s like any other variable name and no type annotation. The first parameter of a method is always an instance of the type that the method is associated with, in this case Fraction, and it is always called ",(0,o.kt)("inlineCode",{parentName:"p"},"self"),". Otherwise this method is the same as our previous standalone function."),(0,o.kt)("p",null,"To call this method, we call it ",(0,o.kt)("em",{parentName:"p"},"on")," an instance of the type using dot notation like in many other languages."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let f1 = Fraction {\n numerator: 1,\n denominator: 2,\n};\n\nlet f2 = Fraction {\n numerator: 1,\n denominator: 2,\n};\n\nlet f3 = f1.multiply(f2);\n")),(0,o.kt)("p",null,"One weakness of the method as it is written above is that it takes ownership of both of the original fractions, and consequently, de-allocates them when it returns. A better signature would only borrow the two original fractions."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(&self, other: &Fraction) -> Fraction {\n Fraction {\n numerator: self.numerator * other.numerator,\n denominator: self.denominator * other.denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"In the previous two versions of this function, we've explicitly listed the type with which the function is associated, namely, ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction"),", several times. While this is perfectly valid Rust, it is also possible to use the type ",(0,o.kt)("inlineCode",{parentName:"p"},"Self")," which has a capital S like other types in Rust. The ",(0,o.kt)("inlineCode",{parentName:"p"},"Self")," is only available in the context of an ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block, and it refers to whatever type the function is associated with. Here's how the function looks when we use the ",(0,o.kt)("inlineCode",{parentName:"p"},"Self")," type."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(self, other: Self) -> Self {\n Self {\n numerator: self.numerator * other.numerator,\n denominator: self.denominator * other.denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"It is also valid, although not particularly idiomatic, to mix some uses of ",(0,o.kt)("inlineCode",{parentName:"p"},"Self")," with some uses of ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction"),"."),(0,o.kt)("h2",{id:"api-methods"},"API Methods"),(0,o.kt)("p",null,"We've seen already that we can access fields of our ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction")," type by using the dot operator and the name of the field. However, this only works if we are in the same module that defines the struct, or if their fields are declared as public with the ",(0,o.kt)("inlineCode",{parentName:"p"},"pub")," keyword. We will discuss visibility and API design later in this unit. But for now, know that it is often not possible to access the fields of types that are defined in foreign code."),(0,o.kt)("p",null,'Rather, it is often the case that a programmer-friendly API is defined on the types to prevent accidentally introducing inconsistent data. Some of the most common such methods are accessor and modifier methods aka "getters" and "setters".'),(0,o.kt)("p",null,"Continuing the example of our ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction")," type, such methods would look like this."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Access the numerator of a given fraction\n fn get_numerator(&self) -> u32{\n self.numerator\n }\n\n /// Change the numerator of a given fraction\n fn set_numerator(&mut self, new_numerator: u32) {\n self.numerator = new_numerator\n }\n}\n")),(0,o.kt)("p",null,"The getter method should contain no surprises as all the relevant concepts were already introduced in our multiply fractions example. The setter however demonstrates that methods can borrow the ",(0,o.kt)("inlineCode",{parentName:"p"},"self")," parameter mutably. Because the ",(0,o.kt)("inlineCode",{parentName:"p"},"set_numerator")," method will mutate the fraction on which it is called, it can only be called if the fraction was defined as mutable with the ",(0,o.kt)("inlineCode",{parentName:"p"},"mut")," keyword."),(0,o.kt)("p",null,"The setter and getter methods very common and very important, but they are also somewhat trivial. Let's consider a more interesting method, one to reduce a fraction to its simplified form. Here we will focus on the signature of the function, and leave the implementation to you, the learner, as an exercise. Notice that it is perfectly acceptable to have multiple ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," blocks for the same type."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Reduce a Fraction in-place by mutating its numerator and denominator.\n fn reduce(&mut self) {\n // Implementation left as an exercise.\n }\n}\n")),(0,o.kt)("p",null,"Like the setter, this method may mutate the fields of the fraction, and thus the signature indicates a mutable borrow."),(0,o.kt)("h2",{id:"associated-functions"},"Associated Functions"),(0,o.kt)("p",null,"Finally we will discuss the concept of associated function. In fact, all methods are associated functions. But not all associated functions are methods. You may remember I said previously that all methods take an instance of the type called ",(0,o.kt)("inlineCode",{parentName:"p"},"self")," as the first parameter. Well a function may still appear in an ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block without this ",(0,o.kt)("inlineCode",{parentName:"p"},"self")," parameter, and such functions are called associated functions, but they are not called method."),(0,o.kt)("p",null,"CAUTION: The jargon here is different than Java. In Java, methods without the ",(0,o.kt)("inlineCode",{parentName:"p"},"this")," (analogous to ",(0,o.kt)("inlineCode",{parentName:"p"},"self"),') parameter are still called methods. In fact they are called static methods. Although the language is different, the concepts are still the same. And if you use the term "static method" Rust programmers are likely to know what you mean, although doing so is technically incorrect.'),(0,o.kt)("p",null,"As an example of a static method, let's consider the API we might provide for a consumer of our Fraction library to create a new fraction."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n fn new(numerator: u32, denominator: u32) -> Self {\n Self{\n numerator: numerator,\n denominator: denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"This function is very straight forward, and simply puts the supplied data into the appropriate fields."),(0,o.kt)("p",null,"I'll take this opportunity to mention a short-hand syntax that is available in Rust and makes the life of the programmer a little nicer. In our new function, we had local variables called ",(0,o.kt)("inlineCode",{parentName:"p"},"numerator")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"denominator")," and we put them into fields with exactly the same names. When assigning a field from a local variable with exactly, the same name, it is valid Rust to elide the field name entirely, and only put the value. The compiler knows what field you are using based on the variable name."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n fn new(numerator: u32, denominator: u32) -> Self {\n Self{\n numerator,\n denominator,\n }\n }\n}\n")),(0,o.kt)("h1",{id:"generics"},"Generics"),(0,o.kt)("p",null,"Many of the types we've defined in this module are more restrictive than they need to be. For example, our Fraction type insists that its inner values be of type ",(0,o.kt)("inlineCode",{parentName:"p"},"u32"),". It may be that in some cases, programmers want more precision and prefer to use ",(0,o.kt)("inlineCode",{parentName:"p"},"u64")," instead. It would be a shame to have to re-write the entire struct and all of its methods just to change all the ",(0,o.kt)("inlineCode",{parentName:"p"},"u32")," to ",(0,o.kt)("inlineCode",{parentName:"p"},"u64"),"."),(0,o.kt)("p",null,"To address this problem, Rust's Type system has a notion of Generic types, or \"generics\" for short. Code that uses generics and related concepts can become quite complex, and we will dive into that full complexity in due course, but for now, let's take a look at a simple use of generic types."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// A fraction type that allows arbitrarily large or small numerators and denominators\nstruct Fraction {\n numerator: T,\n denominator: T,\n}\n")),(0,o.kt)("p",null,"In this improved definition of our Fraction type, we do not explicitly define the type that the numerator and denominator field will be. Rather we say they will be some generic type ",(0,o.kt)("inlineCode",{parentName:"p"},"T"),". We define the single generic type in angle brackets after the name of the struct, ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction"),". This means that ",(0,o.kt)("inlineCode",{parentName:"p"},"T")," can be replaced by any type we may wish to use. While ",(0,o.kt)("inlineCode",{parentName:"p"},"T")," can indeed be any type, because both ",(0,o.kt)("inlineCode",{parentName:"p"},"numerator")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"denominator")," are given the type ",(0,o.kt)("inlineCode",{parentName:"p"},"T"),", that means that they must be the ",(0,o.kt)("em",{parentName:"p"},"same")," type. They cannot be two different types; that would require two generic parameters."),(0,o.kt)("p",null,"When we create instances of our new generic fraction type, we must fill in the generic type."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let my_precise_fraction = Fraction {\n numerator: 236,\n denominator: 473,\n}\n\nlet my_low_memory_fraction = Fraction {\n numerator: 1,\n denominator: 4,\n}\n")),(0,o.kt)("p",null,"We have succeeded in creating a Fraction type that allows us to use any precision integer wa want! But we have also introduced a few problems here. For example, it is now possible to create fractions with data types that don't make any sense at all. Consider this example."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let silly_fraction = Fraction {\n numerator: true,\n denominator: false,\n}\n")),(0,o.kt)("p",null,"Not only does this bool fraction not make any sense, we also won't be able to multiply bool fractions together because booleans themselves can't be multiplied. In the next video we will see how Rust's traits solve this problem."),(0,o.kt)("h1",{id:"traits"},"Traits"),(0,o.kt)("p",null,"Traits are Rusts way of allowing programmers to define shared abstract behavior that may exist on multiple types. This concept is present in most modern programming languages. For example it manifests as ",(0,o.kt)("inlineCode",{parentName:"p"},"interface"),"s in Java, type classes in Haskell, and mix-ins in Ruby."),(0,o.kt)("h2",{id:"area-example"},"Area Example"),(0,o.kt)("p",null,"As a first example, let's consider a few 2D geometry types. Each of these types has different fields, but all of them are have some area, and it is valid to expect that their area can be calculated from their fields. Let's consider a Square and a Circle for example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Square {\n side_length: u32,\n}\n\nstruct Circle {\n radius: u32,\n}\n")),(0,o.kt)("p",null,"Any geometry program is likely to want to know the area of these various shapes. We already know how to implement an ",(0,o.kt)("inlineCode",{parentName:"p"},"area")," method on each of them. But implementing a one-off method on each type is problematic."),(0,o.kt)("p",null,"One surface-level problem is that programmers might choose slightly different names for the area method on each struct."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Square {\n fn get_area(&self) -> u32 {\n //--snip--\n }\n}\n\nimpl Circle {\n fn calculate_area(&self) -> u32 {\n //--snip--\n }\n}\n")),(0,o.kt)("p",null,"A second, more fundamental, problem is that we may want to write a function that can work with ",(0,o.kt)("em",{parentName:"p"},"any")," object whose area can be calculated, and we need a way to express to the compiler that a particular type can have its area calculated. Traits solve both of these problems."),(0,o.kt)("p",null,"Let's define a trait for any type that can have its area calculated."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"trait Area {\n fn area(&self) -> u32;\n}\n")),(0,o.kt)("p",null,"To do this, we use the keyword ",(0,o.kt)("inlineCode",{parentName:"p"},"trait")," and then the name of the trait which starts with a capital letter, just like types do. Inside the body of the trait, we list some function signatures, but we do not include the body. Rather we just end the signature with a semicolon. The body of the function can be different for each type that implements the trait. The concept of writing the function signature without the body should be familiar to C and C++ developers as it is similar to how header files work in those languages."),(0,o.kt)("p",null,"There are a few items other than just function signatures that can go inside a trait definition, but we will save that discussion for later in the course."),(0,o.kt)("p",null,"Let's implement this trait for our two structs."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Area for Square {\n fn area(&self) -> u32 {\n self.side_length * self.side_length\n }\n}\n\nimpl Area for Circle {\n fn area(&self) -> u32 {\n const PI: u32 = 3; // LOL. This is a good approximation, right?\n PI * self.radius * self.radius\n }\n}\n")),(0,o.kt)("p",null,"We can see here that we have solved the shallower naming problem because the trait defines the name of the area function once for every type that implements it."),(0,o.kt)("h2",{id:"trait-bounds-for-function-parameters"},"Trait Bounds for Function parameters"),(0,o.kt)("p",null,"Now let's see how we can solve the deeper problem of writing a function that works with any shape that can have it's area calculated. Imagine that we want to know how many cans of paint we will need to cover a particular shape. For this we use a feature of Rust called a trait bound."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"fn how_many_cans(shape: T) -> u32 {\n /// Each can of paint covers 4 units (eg m^2)\n const one_can: u32 = 4;\n\n // Get the area of the shape in question\n let area_to_paint = shape.area();\n\n // We basically just divide. But we need to be sure there is\n // extra paint, not slightly too little paint.\n (area_to_paint + one_can - 1) / one_can\n}\n")),(0,o.kt)("p",null,"There are a few new things happening in this function signature. The first is the presence of the generic parameter ",(0,o.kt)("inlineCode",{parentName:"p"},"T")," in a function. We previously saw how to declare generics on a struct, and it is quite similar for functions. Any generic types go in angle brackets immediately after the name of the function. But it is more than just a generic parameter. This time we also introduce a trait bound. That's the ",(0,o.kt)("inlineCode",{parentName:"p"},": Area")," part. This syntax says that while the ",(0,o.kt)("inlineCode",{parentName:"p"},"how_many_cans")," function can be used with multiple different types, it can't be used with just any old type. It must be used with a type that implements our ",(0,o.kt)("inlineCode",{parentName:"p"},"Area")," trait."),(0,o.kt)("p",null,"By using this trait bound, we ensure that any type passed to this function implements the necessary area trait. And if you try to pass a different type, for example ",(0,o.kt)("inlineCode",{parentName:"p"},"bool"),", then your code won't compile."),(0,o.kt)("h2",{id:"conditional-method-implementation"},"Conditional Method Implementation"),(0,o.kt)("p",null,"It turns out that trait bounds are the exact tool we need to make our generic Fraction type work. Let's take a look at how we can use trait bounds here."),(0,o.kt)("p",null,"As a reminder, here's where we left our Fraction type."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Fraction {\n numerator: T,\n denominator: T,\n}\n")),(0,o.kt)("p",null,"We discussed that leaving the type ",(0,o.kt)("inlineCode",{parentName:"p"},"T")," unbounded is a problem because a user could try to use the type ",(0,o.kt)("inlineCode",{parentName:"p"},"bool"),". Such a fraction does not make any sense at all, but concretely it doesn't make sense because bools, can't be multiplied. Let's see if the compiler can tell us that itself."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(&self, other: &Self) -> Self {\n Self {\n numerator: self.numerator * other.numerator,\n denominator: self.denominator * other.denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"First notice the syntax for implementing a method on a struct that has generics. we start with ",(0,o.kt)("inlineCode",{parentName:"p"},"impl"),' which can be understood as "for all types, ',(0,o.kt)("inlineCode",{parentName:"p"},"T"),", implement the following. Then, as before, we name the type that we are associating this method with."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-text"},"error[E0369]: cannot multiply `T` by `T`\n --\x3e src/main.rs:10:39\n |\n10 | numerator: self.numerator * other.numerator,\n | -------------- ^ --------------- T\n | |\n | T\n |\nhelp: consider restricting type parameter `T`\n |\n5 | impl> Fraction {\n | +++++++++++++++++++++++++\n\n")),(0,o.kt)("p",null,'Indeed, the compiler tells us that we "cannot multiply ',(0,o.kt)("inlineCode",{parentName:"p"},"T")," by ",(0,o.kt)("inlineCode",{parentName:"p"},"T"),'". It also offers a very helpful suggestion using the ',(0,o.kt)("inlineCode",{parentName:"p"},"Mul")," trait from the standard library. The compiler's suggestion will work here, and I encourage you to confirm that by making the suggested change. However, the understand the ",(0,o.kt)("inlineCode",{parentName:"p"},"Mul")," trait, we need to first learn about associated types, which we will not cover until later in the course. So for now, let's invent our own ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply")," trait for any type that can be multiplied together, and implement it for a few primitive unsigned integer types."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"trait Multiply {\n fn multiply(&self, other: &Self) -> Self;\n}\n\nimpl Multiply for u8 {\n fn multiply(&self, other: &Self) -> Self {\n self * other\n }\n}\n\nimpl Multiply for u16 {\n fn multiply(&self, other: &Self) -> Self {\n self * other\n }\n}\n\nimpl Multiply for u32 {\n fn multiply(&self, other: &Self) -> Self {\n self * other\n }\n}\n\n// Could also so u64, u128, but the point is made.\n")),(0,o.kt)("p",null,"We can now tell the compiler that our fraction multiplication method can be used for any fraction whose inner values implement this ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply")," trait."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(&self, other: &Self) -> Self {\n Self {\n numerator: self.numerator.multiply(&other.numerator),\n denominator: self.denominator.multiply(&other.denominator),\n }\n }\n}\n")),(0,o.kt)("p",null,"Of course, we now need to call the ",(0,o.kt)("inlineCode",{parentName:"p"},"multiply")," method instead of just using the ",(0,o.kt)("inlineCode",{parentName:"p"},"*")," operator. But the more interesting difference is the trait bound ",(0,o.kt)("inlineCode",{parentName:"p"},"T: Multiply"),'. This can be understood as "For all types ',(0,o.kt)("inlineCode",{parentName:"p"},"T")," that implement the ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply"),' trait, implement this method".'),(0,o.kt)("p",null,"Our fraction type is now able to support multiplication as long as the inner data type also supports it, and we communicate that through a trait bound."),(0,o.kt)("h1",{id:"trait-bounds-on-type-definitions"},"Trait Bounds on Type Definitions"),(0,o.kt)("p",null,"In the previous video we showed how to use a trait bound on the ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block where we associated the ",(0,o.kt)("inlineCode",{parentName:"p"},"multiply")," method with our Fraction type. In this video I want to dig on on what ",(0,o.kt)("em",{parentName:"p"},"exactly")," we did there, and what we didn't do yet."),(0,o.kt)("p",null,"In fact, we did ",(0,o.kt)("em",{parentName:"p"},"not")," prohibit creating instances of type ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction"),". With the code from the previous video it is still perfectly possible to create such bool fractions. But what we did do is express to the compiler that for certain generic types, it is now possible to multiply fractions together. This is an interesting and powerful feature of Rust. We were able to conditionally associate a method with our ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction")," type depending on whether or not its generic types meets a particular trait bound. If the generic type implements the ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply")," trait, the the instance has a ",(0,o.kt)("inlineCode",{parentName:"p"},"multiply")," method. But if the generic type does not implement this trait, then the method will not be associated."),(0,o.kt)("p",null,"Having observed this powerful aspect of Rust, you may be thinking, \"Yeah, that is useful and interesting, but in this case, do we really want to allow even creating weird fractions that can't be multiplied? Well if you don't we can express that in Rust as well. All we have to do is add... you guessed it, a trait bound... to the struct definition."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Fraction {\n numerator: T,\n denominator: T,\n}\n")),(0,o.kt)("p",null,"By adding the trait bound to the struct definition, we have now made it impossible to even create a Fraction whose generic type cannot be multiplied. We do still need to leave the bound on the impl block as well though."),(0,o.kt)("h2",{id:"implementing-multiply-for-our-fraction-type"},"Implementing Multiply for our Fraction type"),(0,o.kt)("p",null,"It may have occurred to you that since our Fraction type is able to be multiplied now, perhaps we should implement our Multiply trait for it rather than just associating a one-off method. I totally agree, so let's do it. It actually only takes a small adjustment to our previous impl block."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Multiply for Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(&self, other: &Self) -> Self {\n Self {\n numerator: self.numerator.multiply(&other.numerator),\n denominator: self.denominator.multiply(&other.denominator),\n }\n }\n}\n")),(0,o.kt)("p",null,"All we had to do was change the impl block to ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block. We still have the same trait bound as before, but we've added ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply for")," indicating we're now implementing a trait. Since our function signature already matched the trait's expected signature, we are done. If we had previously called out one-off method something different like \"times\" or something, we would have to make minor adjustments there as well."),(0,o.kt)("h2",{id:"limitation"},"Limitation"),(0,o.kt)("p",null,"As a final note, I'll mention one property of Rust's type system that sometimes feels like a limitation. You can only implement a trait for a type if ",(0,o.kt)("em",{parentName:"p"},"either")," the trait or the type (or both) are defined in the same crate as the implementation. While this can sometimes feel like a limitation, it is actually very important to prohibit this. If we allowed implementing Foreign traits on Foreign types, then it may be that multiple conflicting implementation exist in different crates, and the compiler would be unable to catch this because it checks one crate at a time."),(0,o.kt)("p",null,"It is unlikely that you'll encounter this limitation until you have a little more rust experience under your belt. But when you do encounter it, know that there is a work around, and it is to use the new type pattern that we discussed previously, and then implement the trait on your new type."),(0,o.kt)("h1",{id:"some-common-traits-and-collections"},"Some Common Traits and Collections"),(0,o.kt)("p",null,"In this module we have discussed how to create your own types, both structs and enums, create your own traits for abstracting shared behavior, and implement your traits on your types. In this final video, we'll take a look through some of the most common types and traits that are defined in the standard library and how you might encounter them."),(0,o.kt)("p",null,"TODO flesh this out."),(0,o.kt)("h2",{id:"types"},"Types"),(0,o.kt)("p",null,"Vec\nString\nBTreeMap\nBTreeSet - Note there are also hashtable based maps and sets with similar interfaces, but we will not be looking at them because they are not available in the Substrate Runtime."),(0,o.kt)("h2",{id:"traits-1"},"Traits"),(0,o.kt)("p",null,"Iterator\nDisplay\nDebug\nDefault"))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ddd8c84d.73e2251a.js b/assets/js/ddd8c84d.73e2251a.js deleted file mode 100644 index ec9c6c0e1..000000000 --- a/assets/js/ddd8c84d.73e2251a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[8845],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var a=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},m="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=u(n),h=o,d=m["".concat(s,".").concat(h)]||m[h]||c[h]||i;return n?a.createElement(d,r(r({ref:t},p),{},{components:n})):a.createElement(d,r({ref:t},p))}));function d(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,r=new Array(i);r[0]=h;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:o,r[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>r,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var a=n(7462),o=(n(7294),n(3905));const i={id:"module3",sidebar_position:1},r="3. Intro to Intermediate Rust",l={unversionedId:"Rust/section3/module3",id:"Rust/section3/module3",title:"3. Intro to Intermediate Rust",description:"Structs. Enums. Methods. Packages and Crates.",source:"@site/docs/Rust/section3/module3.md",sourceDirName:"Rust/section3",slug:"/Rust/section3/module3",permalink:"/docs/Rust/section3/module3",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Rust/section3/module3.md",tags:[],version:"current",lastUpdatedBy:"Bader Youssef",lastUpdatedAt:1678293818,formattedLastUpdatedAt:"Mar 8, 2023",sidebarPosition:1,frontMatter:{id:"module3",sidebar_position:1}},s={},u=[{value:"API Methods",id:"api-methods",level:2},{value:"Associated Functions",id:"associated-functions",level:2},{value:"Area Example",id:"area-example",level:2},{value:"Trait Bounds for Function parameters",id:"trait-bounds-for-function-parameters",level:2},{value:"Conditional Method Implementation.",id:"conditional-method-implementation",level:2},{value:"Implementing Multiply for our Fraction type",id:"implementing-multiply-for-our-fraction-type",level:2},{value:"Limitation",id:"limitation",level:2},{value:"Types",id:"types",level:2},{value:"Traits",id:"traits-1",level:2}],p={toc:u},m="wrapper";function c(e){let{components:t,...n}=e;return(0,o.kt)(m,(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"3-intro-to-intermediate-rust"},"3. Intro to Intermediate Rust"),(0,o.kt)("p",null,"Structs. Enums. Methods. Packages and Crates.\nCargo feature - this will be important for the \u201cstd\u201d features all over Substrate. \u201cruntime-benchmarks\u201d and \u201ctry-runtime\u201d."),(0,o.kt)("p",null,"In this module we will begin to explore Rust's type system. We will create our own types and add methods to those types. We will also take our first look at Generics and Traits in Rust. There will be much more to cover about the type system later in the course, but in this module we will learn the most fundamental and common aspects."),(0,o.kt)("p",null,'Rust allows users to create two different broad categories of types. First, there are Structs which are sometimes known as "product types" or "each-of types" because a struct instance requires data in each of its fields. Second, there are Enums which are sometimes known as "sum types" or "one-of types" because an enum instance is one of multiple variants.'),(0,o.kt)("p",null,"If you come from an object oriented programming background, structs will be familiar as they are pretty similar to the data fields on a class or object. If you come from an functional programming background, both structs and enums will likely be familiar, although the enum syntax is unlike that of OCaml or Haskell."),(0,o.kt)("h1",{id:"structs"},"Structs"),(0,o.kt)("p",null,"Structs allow packaging multiple primitive data types together into a logical package. They are very similar to the tuples we saw previously, although they make the language much more programmer friendly."),(0,o.kt)("p",null,"When defining a struct, you provide a name for the type, which has a capital letter by convention. Then you provide a name for each field and a type for each field."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Fraction {\n numerator: u32,\n denominator: u32,\n}\n")),(0,o.kt)("p",null,"To create an instance of a struct, you use the name of the type and the name of each field as before. But instead of specifying a type for each field, which has already been defined, we supply a value for each field."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let one_half = Fraction {\n numerator: 1,\n denominator: 2,\n}\n")),(0,o.kt)("p",null,"Structs, like tuples, need to have a value for each and every field. It is not valid to omit one of the fields."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let one = Fraction {\n numerator: 1,\n}\n")),(0,o.kt)("p",null,"Similarly, each field must be of the type that was declared when the struct was defined."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let one_half = Fraction {\n numerator: 1u64,\n denominator: 2u64,\n}\n")),(0,o.kt)("p",null,"Now that we know how declare and instantiate structs, let's see how to access their fields. To access the inner data of a struct, we just use a dot operator and then the name of the field."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let my_frac = Fraction {\n numerator: 2,\n denominator: 3,\n}\n\nlet my_bottom_value = my_frac.denominator;\nassert!(my_bottom_value == 3);\n")),(0,o.kt)("p",null,"Notice that here we have used simple integer literals like ",(0,o.kt)("inlineCode",{parentName:"p"},"3")," as opposed to explicitly typed ones like ",(0,o.kt)("inlineCode",{parentName:"p"},"3u32"),". This is allowed because the Rust compiler knows that the fields of the ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction")," type are ",(0,o.kt)("inlineCode",{parentName:"p"},"u32")," because they were declared as such. Similarly, the compiler can infer that the type of ",(0,o.kt)("inlineCode",{parentName:"p"},"my_bottom_value")," is u32 so we don't need an explicit annotation."),(0,o.kt)("p",null,"We can use the same syntax to mutate the value of a field. But, as always in Rust, to make a value mutable it must be explicitly declared as such."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let mut f1 = Fraction {\n numerator: 1,\n denominator: 2,\n};\n\nlet f2 = Fraction {\n numerator: 1,\n denominator: 2,\n};\n\n// here we can mutate the denominator of f1\nf1.denominator = 3;\n\n// but this line will not compile because f2 is immutable\nf2.denominator = 4;\n")),(0,o.kt)("h1",{id:"tuple-structs"},"Tuple Structs"),(0,o.kt)("p",null,"In some cases you want to give your type a name like you can with structs, but you don't want to give each field a name. One such example would be storing a point on a 2D lattice."),(0,o.kt)("p",null,"So far we've learned two ways to store this data, both of which work, but neither of which are exactly what we want. We could simply use a tuple, but this does not allow us\nto name the type, and could lead to confusion and even bugs if tuples are used in another context in the program.\nOr we could use a struct, but this require repeating field names often when the position is a well-established mathematical convention."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"// We could simply use a tuple.\nlet point = (2u32, 3u32);\n\n// We could use a struct\nstruct Point {\n x_coord: u32,\n y_coord: u32,\n}\n")),(0,o.kt)("p",null,"For cases like this, Rust has the tuple struct. This allows us to name the type while referencing the fields positionally."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Point(u32, u32);\n")),(0,o.kt)("p",null,"When using tuple structs, the fields are accessed with the dot operator and an integer index exactly like they are when working with tuples."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let buried_treasure = Point(4, 5);\n\nlet treasure_x_coord = buried_treasure.0;\n")),(0,o.kt)("p",null,'It is also possible to create tuple structs with a single element inside. You may wonder why you would ever want to do this when you could simply use the underlying type directly, but in fact this is done frequently, and is often referred to as the "new type pattern". The new type pattern allows us the leverage Rust\'s type system to distinguish values of different semantic units even if they are represented digitally by the same underlying primitive type.'),(0,o.kt)("p",null,"A typical example would be something like storing Temperature data when you want to be sure that the units are in Celsius as opposed to something like Kelvin or Fahrenheit."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// A temperature value that is stored in the celsius units.\nstruct Celsius(u32);\n")),(0,o.kt)("p",null,"As a side note, notice the comment starting with a triple slash here. This is known as a doc comment or a documentation comment. Doc comments can precede data types, functions, and many other pieces of code and be used to generate automated documentation. We will not explore this in depth here, but know that it is good practice to use doc comments when creating your own types."),(0,o.kt)("p",null,"Finally, it is possible to create unit structs that have no fields at all. They are analogous to Rust's built-in unit type ",(0,o.kt)("inlineCode",{parentName:"p"},"()"),". This is useful when you want a type to declare static methods on. We will cover methods later in this module."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// A unit type that has no data fields.\nstruct MyUnitType;\n")),(0,o.kt)("h1",{id:"custom-types-in-functions-and-other-types"},"Custom types in Functions and Other Types"),(0,o.kt)("p",null,"Now that we've created some custom types of our own, let's see some ways that they can be used."),(0,o.kt)("p",null,"One way we can use our custom types is as parameters to functions. Let's define a function that allows us to multiply two fractions."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"fn multiply_fractions(a: Fraction, b: Fraction) -> Fraction {\n Fraction {\n numerator: a.numerator * b.numerator,\n denominator: a.denominator * b.denominator,\n }\n}\n")),(0,o.kt)("p",null,"Now we are ready to see the value of the new type pattern we discussed previously. Consider steam engine control circuit with a function that determines whether the boiler has warmed up enough to start the engine."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// First attempt that is subject to unit confusion\nfn warm_enough(temp: u32) -> bool {\n temp > 100\n}\n")),(0,o.kt)("p",null,"In this naive function we run the risk of a user entering a Fahrenheit temperature by mistake, or interfacing with a temperature probe that is incorrectly configured to report Fahrenheit temperature."),(0,o.kt)("p",null,"We can make this function safer by using Rust's type system to insist that a Celsius temperate is entered. This program will not even compile if a plain u32 is passed."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Celsius(u32);\n\nfn warm_enough(temp: Celsius) -> bool {\n temp.0 > 100\n}\n")),(0,o.kt)("p",null,"Another place we can use our custom types is as fields of other custom types. Consider a geometry program that builds up shapes from more fundamental primitive types."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Point(u32, u32);\n\n/// We define a Rectangle by its two opposite corners\nstruct Rectangle {\n top_left: Point,\n bottom_right: Point,\n}\n")),(0,o.kt)("h1",{id:"enums"},"Enums"),(0,o.kt)("p",null,"TODO"),(0,o.kt)("h1",{id:"methods"},"Methods"),(0,o.kt)("p",null,"We saw previously how we can write functions that take our custom data types as parameters or return our custom data types. Rust also supports the notion of a method. A method is a special function that is defined in the context of a particular type."),(0,o.kt)("p",null,"If you are familiar with Object Oriented languages like Java, methods will be very familiar. In Rust, methods are defined in a separate block than the type itself, unlike Java where the methods and data are all part of the same class. This adds some flexibility to Rust as we will see. Methods always take function "),(0,o.kt)("p",null,"Methods are very similar to functions in syntax. The only difference is that they go in an ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block. As an illustrative example, lets re-write the ",(0,o.kt)("inlineCode",{parentName:"p"},"multiply_fraction")," function the we looked at earlier as a method."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(self, other: Fraction) -> Fraction {\n Fraction {\n numerator: self.numerator * other.numerator,\n denominator: self.denominator * other.denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"This method is very similar to the function we wrote previously. The main difference is the first parameter, ",(0,o.kt)("inlineCode",{parentName:"p"},"self"),". Notice that this ",(0,o.kt)("inlineCode",{parentName:"p"},"self")," has a lowercase s like any other variable name and no type annotation. The first parameter of a method is always an instance of the type that the method is associated with, in this case Fraction, and it is always called ",(0,o.kt)("inlineCode",{parentName:"p"},"self"),". Otherwise this method is the same as our previous standalone function."),(0,o.kt)("p",null,"To call this method, we call it ",(0,o.kt)("em",{parentName:"p"},"on")," an instance of the type using dot notation like in many other languages."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let f1 = Fraction {\n numerator: 1,\n denominator: 2,\n};\n\nlet f2 = Fraction {\n numerator: 1,\n denominator: 2,\n};\n\nlet f3 = f1.multiply(f2);\n")),(0,o.kt)("p",null,"One weakness of the method as it is written above is that it takes ownership of both of the original fractions, and consequently, de-allocates them when it returns. A better signature would only borrow the two original fractions."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(&self, other: &Fraction) -> Fraction {\n Fraction {\n numerator: self.numerator * other.numerator,\n denominator: self.denominator * other.denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"In the previous two versions of this function, we've explicitly listed the type with which the function is associated, namely, ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction"),", several times. While this is perfectly valid Rust, it is also possible to use the type ",(0,o.kt)("inlineCode",{parentName:"p"},"Self")," which has a capital S like other types in Rust. The ",(0,o.kt)("inlineCode",{parentName:"p"},"Self")," is only available in the context of an ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block, and it refers to whatever type the function is associated with. Here's how the function looks when we use the ",(0,o.kt)("inlineCode",{parentName:"p"},"Self")," type."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(self, other: Self) -> Self {\n Self {\n numerator: self.numerator * other.numerator,\n denominator: self.denominator * other.denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"It is also valid, although not particularly idiomatic, to mix some uses of ",(0,o.kt)("inlineCode",{parentName:"p"},"Self")," with some uses of ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction"),"."),(0,o.kt)("h2",{id:"api-methods"},"API Methods"),(0,o.kt)("p",null,"We've seen already that we can access fields of our ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction")," type by using the dot operator and the name of the field. However, this only works if we are in the same module that defines the struct, or if thei fields are declared as public with the ",(0,o.kt)("inlineCode",{parentName:"p"},"pub")," keyword. We will discuss visibility and API design later in this unit. But for now, know that it is often not possible to access the fields of types that are defined in foreign code."),(0,o.kt)("p",null,'Rather, it is often the case that a programmer-friendly API is defined on the types to prevent accidentally introducing inconsistent data. Some of the most common such methods are accessor and modifier methods aka "getters" and "setters".'),(0,o.kt)("p",null,"Continuing the example of our ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction")," type, such methods would look like this."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Access the numerator of a given fraction\n fn get_numerator(&self) -> u32{\n self.numerator\n }\n\n /// Change the numerator of a given fraction\n fn set_numerator(&mut self, new_numerator: u32) {\n self.numerator = new_numerator\n }\n}\n")),(0,o.kt)("p",null,"The getter method should contain no surprises as all the relevant concepts were already introduced in our multiply fractions example. The setter however demonstrates that methods can borrow the ",(0,o.kt)("inlineCode",{parentName:"p"},"self")," parameter mutably. Because the ",(0,o.kt)("inlineCode",{parentName:"p"},"set_numerator")," method will mutate the fraction on which it is called, it can only be called if the fraction was defined as mutable with the ",(0,o.kt)("inlineCode",{parentName:"p"},"mut")," keyword."),(0,o.kt)("p",null,"The setter and getter methods very common and very important, but they are also somewhat trivial. Let's consider a more interesting method, one to reduce a fraction to its simplified form. Here we will focus on the signature of the function, and leave the implementation to you, the learner, as an exercise. Notice that it is perfectly acceptable to have multiple ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," blocks for the same type."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Reduce a Fraction in-place by mutating its numerator and denominator.\n fn reduce(&mut self) {\n // Implementation left as an exercise.\n }\n}\n")),(0,o.kt)("p",null,"Like the setter, this method may mutate the fields of the fraction, and thus the signature indicates a mutable borrow."),(0,o.kt)("h2",{id:"associated-functions"},"Associated Functions"),(0,o.kt)("p",null,"Finally we will discuss the concept of associated function. In fact, all methods are associated functions. But not all associated functions are methods. You may remember I said previously that all methods take an instance of the type called ",(0,o.kt)("inlineCode",{parentName:"p"},"self")," as the first parameter. Well a function may still appear in an ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block without this ",(0,o.kt)("inlineCode",{parentName:"p"},"self")," parameter, and such functions are called associated functions, but they are not called method."),(0,o.kt)("p",null,"CAUTION: The jargon here is different than Java. In Java, methods without the ",(0,o.kt)("inlineCode",{parentName:"p"},"this")," (analogous to ",(0,o.kt)("inlineCode",{parentName:"p"},"self"),') parameter are still called methods. In fact they are called static methods. Although the language is different, the concepts are still the same. And if you use the term "static method" Rust programmers are likely to know what you mean, although doing so is technically incorrect.'),(0,o.kt)("p",null,"As an example of a static method, let's consider the API we might provide for a consumer of our Fraction library to create a new fraction."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n fn new(numerator: u32, denominator: u32) -> Self {\n Self{\n numerator: numerator,\n denominator: denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"This function is very straight forward, and simply puts the supplied data into the appropriate fields."),(0,o.kt)("p",null,"I'll take this opportunity to mention a short-hand syntax that is available in Rust and makes the life of the programmer a little nicer. In our new function, we had local variables called ",(0,o.kt)("inlineCode",{parentName:"p"},"numerator")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"denominator")," and we put them into fields with exactly the same names. When assigning a field from a local variable with exactly, the same name, it is valid Rust to elide the field name entirely, and only put the value. The compiler knows what field you are using based on the variable name."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n fn new(numerator: u32, denominator: u32) -> Self {\n Self{\n numerator,\n denominator,\n }\n }\n}\n")),(0,o.kt)("h1",{id:"generics"},"Generics"),(0,o.kt)("p",null,"Many of the types we've defined in this module are more restrictive than they need to be. For example, our Fraction type insists that its inner values be of type ",(0,o.kt)("inlineCode",{parentName:"p"},"u32"),". It may be that in some cases, programmers want more precision and prefer to use ",(0,o.kt)("inlineCode",{parentName:"p"},"u64")," instead. It would be a shame to have to re-write the entire struct and all of its methods just to change all the ",(0,o.kt)("inlineCode",{parentName:"p"},"u32")," to ",(0,o.kt)("inlineCode",{parentName:"p"},"u64"),"."),(0,o.kt)("p",null,"To address this problem, Rust's Type system has a notion of Generic types, or \"generics\" for short. Code that uses generics and related concepts can become quite complex, and we will dive into that full complexity in due course, but for now, let's take a look at a simple use of generic types."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// A fraction type that allows arbitrarily large or small numerators and denominators\nstruct Fraction {\n numerator: T,\n denominator: T,\n}\n")),(0,o.kt)("p",null,"In this improved definition of our Fraction type, we do not explicitly define the type that the numerator and denominator field will be. Rather we say they will be some generic type ",(0,o.kt)("inlineCode",{parentName:"p"},"T"),". We define the single generic type in angle brackets after the name of the struct, ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction"),". This means that ",(0,o.kt)("inlineCode",{parentName:"p"},"T")," can be replaced by any type we may wish to use. While ",(0,o.kt)("inlineCode",{parentName:"p"},"T")," can indeed be any type, because both ",(0,o.kt)("inlineCode",{parentName:"p"},"numerator")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"denominator")," are given the type ",(0,o.kt)("inlineCode",{parentName:"p"},"T"),", that means that they must be the ",(0,o.kt)("em",{parentName:"p"},"same")," type. They cannot be two different types; that would require two generic parameters."),(0,o.kt)("p",null,"When we create instances of our new generic fraction type, we must fill in the generic type."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let my_precise_fraction = Fraction {\n numerator: 236,\n denominator: 473,\n}\n\nlet my_low_memory_fraction = Fraction {\n numerator: 1,\n denominator: 4,\n}\n")),(0,o.kt)("p",null,"We have succeeded in creating a Fraction type that allows us to use any precision integer wa want! But we have also introduced a few problems here. For example, it is now possible to create fractions with data types that don't make any sense at all. Consider this example."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"let silly_fraction = Fraction {\n numerator: true,\n denominator: false,\n}\n")),(0,o.kt)("p",null,"Not only does this bool fraction not make any sense, we also won't be able to multiply bool fractions together because booleans themselves can't be multiplied. In the next video we will see how Rust's traits solve this problem."),(0,o.kt)("h1",{id:"traits"},"Traits"),(0,o.kt)("p",null,"Traits are Rusts way of allowing programmers to define shared abstract behavior that may exist on multiple types. This concept is present in most modern programming languages. For example it manifests as ",(0,o.kt)("inlineCode",{parentName:"p"},"interface"),"s in Java, type classes in Haskell, and mix-ins in Ruby."),(0,o.kt)("h2",{id:"area-example"},"Area Example"),(0,o.kt)("p",null,"As a first example, let's consider a few 2D geometry types. Each of these types has different fields, but all of them are have some area, and it is valid to expect that their area can be calculated from their fields. Let's consider a Square and a Circle for example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Square {\n side_length: u32,\n}\n\nstruct Circle {\n radius: u32,\n}\n")),(0,o.kt)("p",null,"Any geometry program is likely to want to know the area of these various shapes. We already know how to implement an ",(0,o.kt)("inlineCode",{parentName:"p"},"area")," method on each of them. But implementing a one-off method on each type is problematic."),(0,o.kt)("p",null,"One surface-level problem is that programmers might choose slightly different names for the area method on each struct."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Square {\n fn get_area(&self) -> u32 {\n //--snip--\n }\n}\n\nimpl Circle {\n fn calculate_area(&self) -> u32 {\n //--snip--\n }\n}\n")),(0,o.kt)("p",null,"A second, more fundamental, problem is that we may want to write a function that can work with ",(0,o.kt)("em",{parentName:"p"},"any")," object whose area can be calculated, and we need a way to express to the compiler that a particular type can have its area calculated. Traits solve both of these problems."),(0,o.kt)("p",null,"Let's define a trait for any type that can have its area calculated."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"trait Area {\n fn area(&self) -> u32;\n}\n")),(0,o.kt)("p",null,"To do this, we use the keyword ",(0,o.kt)("inlineCode",{parentName:"p"},"trait")," and then the name of the trait which starts with a capital letter, just like types do. Inside the body of the trait, we list some function signatures, but we do not include the body. Rather we just end the signature with a semicolon. The body of the function can be different for each type that implements the trait. The concept of writing the function signature without the body should be familiar to C and C++ developers as it is similar to how header files work in those languages."),(0,o.kt)("p",null,"There are a few items other than just function signatures that can go inside a trait definition, but we will save that discussion for later in the course."),(0,o.kt)("p",null,"Let's implement this trait for our two structs."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Area for Square {\n fn area(&self) -> u32 {\n self.side_length * self.side_length\n }\n}\n\nimpl Area for Circle {\n fn area(&self) -> u32 {\n const PI: u32 = 3; // LOL. This is a good approximation, right?\n PI * self.radius * self.radius\n }\n}\n")),(0,o.kt)("p",null,"We can see here that we have solved the shallower naming problem because the trait defines the name of the area function once for every type that implements it."),(0,o.kt)("h2",{id:"trait-bounds-for-function-parameters"},"Trait Bounds for Function parameters"),(0,o.kt)("p",null,"Now let's see how we can solve the deeper problem of writing a function that works with any shape that can have it's area calculated. Imagine that we want to know how many cans of paint we will need to cover a particular shape. For this we use a feature of Rust called a trait bound."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"fn how_many_cans(shape: T) -> u32 {\n /// Each can of paint covers 4 units (eg m^2)\n const one_can: u32 = 4;\n\n // Get the area of the shape in question\n let area_to_paint = shape.area();\n\n // We basically just divide. But we need to be sure there is\n // extra paint, not slightly too little paint.\n (area_to_paint + one_can - 1) / one_can\n}\n")),(0,o.kt)("p",null,"There are a few new things happening in this function signature. The first is the presence of the generic parameter ",(0,o.kt)("inlineCode",{parentName:"p"},"T")," in a function. We previously saw how to declare generics on a struct, and it is quite similar for functions. Any generic types go in angle brackets immediately after the name of the function. But it is more than just a generic parameter. This time we also introduce a trait bound. That's the ",(0,o.kt)("inlineCode",{parentName:"p"},": Area")," part. This syntax says that while the ",(0,o.kt)("inlineCode",{parentName:"p"},"how_many_cans")," function can be used with multiple different types, it can't be used with just any old type. It must be used with a type that implements our ",(0,o.kt)("inlineCode",{parentName:"p"},"Area")," trait."),(0,o.kt)("p",null,"By using this trait bound, we ensure that any type passed to this function implements the necessary area trait. And if you try to pass a different type, for example ",(0,o.kt)("inlineCode",{parentName:"p"},"bool"),", then your code won't compile."),(0,o.kt)("h2",{id:"conditional-method-implementation"},"Conditional Method Implementation."),(0,o.kt)("p",null,"It turns out that trait bounds are the exact tool we need to make our generic Fraction type work. Let's take a look at how we can use trait bounds here."),(0,o.kt)("p",null,"As a reminder, here's where we left our Fraction type."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Fraction {\n numerator: T,\n denominator: T,\n}\n")),(0,o.kt)("p",null,"We discussed that leaving the type ",(0,o.kt)("inlineCode",{parentName:"p"},"T")," unbounded is a problem because a user could try to use the type ",(0,o.kt)("inlineCode",{parentName:"p"},"bool"),". Such a fraction does not make any sense at all, but concretely it doesn't make sense because bools, can't be multiplied. Let's see if the compiler can tell us that itself."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(&self, other: &Self) -> Self {\n Self {\n numerator: self.numerator * other.numerator,\n denominator: self.denominator * other.denominator,\n }\n }\n}\n")),(0,o.kt)("p",null,"First notice the syntax for implementing a method on a struct that has generics. we start with ",(0,o.kt)("inlineCode",{parentName:"p"},"impl"),' which can be understood as "for all types, ',(0,o.kt)("inlineCode",{parentName:"p"},"T"),", implement the following. Then, as before, we name the type that we are associating this method with. "),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-text"},"error[E0369]: cannot multiply `T` by `T`\n --\x3e src/main.rs:10:39\n |\n10 | numerator: self.numerator * other.numerator,\n | -------------- ^ --------------- T\n | |\n | T\n |\nhelp: consider restricting type parameter `T`\n |\n5 | impl> Fraction {\n | +++++++++++++++++++++++++\n\n")),(0,o.kt)("p",null,'Indeed, the compiler tells us that we "cannot multiply ',(0,o.kt)("inlineCode",{parentName:"p"},"T")," by ",(0,o.kt)("inlineCode",{parentName:"p"},"T"),'". It also offers a very helpful suggestion using the ',(0,o.kt)("inlineCode",{parentName:"p"},"Mul")," trait from the standard library. The compiler's suggestion will work here, and I encourage you to confirm that by making the suggested change. However, the understand the ",(0,o.kt)("inlineCode",{parentName:"p"},"Mul")," trait, we need to first learn about associated types, which we will not cover until later in the course. So for now, let's invent our own ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply")," trait for any type that can be multiplied together, and implement it for a few primitive unsigned integer types."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"trait Multiply {\n fn multiply(&self, other: &Self) -> Self;\n}\n\nimpl Multiply for u8 {\n fn multiply(&self, other: &Self) -> Self {\n self * other\n }\n}\n\nimpl Multiply for u16 {\n fn multiply(&self, other: &Self) -> Self {\n self * other\n }\n}\n\nimpl Multiply for u32 {\n fn multiply(&self, other: &Self) -> Self {\n self * other\n }\n}\n\n// Could also so u64, u128, but the point is made.\n")),(0,o.kt)("p",null,"We can now tell the compiler that our fraction multiplication method can be used for any fraction whose inner values implement this ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply")," trait."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(&self, other: &Self) -> Self {\n Self {\n numerator: self.numerator.multiply(&other.numerator),\n denominator: self.denominator.multiply(&other.denominator),\n }\n }\n}\n")),(0,o.kt)("p",null,"Of course, we now need to call the ",(0,o.kt)("inlineCode",{parentName:"p"},"multiply")," method instead of just using the ",(0,o.kt)("inlineCode",{parentName:"p"},"*")," operator. But the more interesting difference is the trait bound ",(0,o.kt)("inlineCode",{parentName:"p"},"T: Multiply"),'. This can be understood as "For all types ',(0,o.kt)("inlineCode",{parentName:"p"},"T")," that implement the ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply"),' trait, implement this method".'),(0,o.kt)("p",null,"Our fraction type is now able to support multiplication as long as the inner data type also supports it, and we communicate that through a trait bound."),(0,o.kt)("h1",{id:"trait-bounds-on-type-definitions"},"Trait Bounds on Type Definitions"),(0,o.kt)("p",null,"In the previous video we showed how to use a trait bound on the ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block where we associated the ",(0,o.kt)("inlineCode",{parentName:"p"},"multiply")," method with our Fraction type. In this video I want to dig on on what ",(0,o.kt)("em",{parentName:"p"},"exactly")," we did there, and what we didn't do yet."),(0,o.kt)("p",null,"In fact, we did ",(0,o.kt)("em",{parentName:"p"},"not")," prohibit creating instances of type ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction"),". With the code from the previous video it is still perfectly possible to create such bool fractions. But what we did do is express to the compiler that for certain generic types, it is now possible to multiply fractions together. This is an interesting and powerful feature of Rust. We were able to conditionally associate a method with our ",(0,o.kt)("inlineCode",{parentName:"p"},"Fraction")," type depending on whether or not its generic types meets a particular trait bound. If the generic type implements the ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply")," trait, the the instance has a ",(0,o.kt)("inlineCode",{parentName:"p"},"multiply")," method. But if the generic type does not implement this trait, then the method will not be associated."),(0,o.kt)("p",null,"Having observed this powerful aspect of Rust, you may be thinking, \"Yeah, that is useful and interesting, but in this case, do we really want to allow even creating weird fractions that can't be multiplied? Well if you don't we can express that in Rust as well. All we have to do is add... you guessed it, a trait bound... to the struct definition."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"struct Fraction {\n numerator: T,\n denominator: T,\n}\n")),(0,o.kt)("p",null,"By adding the trait bound to the struct definition, we have now made it impossible to even create a Fraction whose generic type cannot be multiplied. We do still need to leave the bound on the impl block as well though."),(0,o.kt)("h2",{id:"implementing-multiply-for-our-fraction-type"},"Implementing Multiply for our Fraction type"),(0,o.kt)("p",null,"It may have occurred to you that since our Fraction type is able to be multiplied now, perhaps we should implement our Multiply trait for it rather than just associating a one-off method. I totally agree, so let's do it. It actually only takes a small adjustment to our previous impl block."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"impl Multiply for Fraction {\n /// Multiply two fractions together, and return the Product\n /// as a third new fraction\n fn multiply(&self, other: &Self) -> Self {\n Self {\n numerator: self.numerator.multiply(&other.numerator),\n denominator: self.denominator.multiply(&other.denominator),\n }\n }\n}\n")),(0,o.kt)("p",null,"All we had to do was change the impl block to ",(0,o.kt)("inlineCode",{parentName:"p"},"impl")," block. We still have the same trait bound as before, but we've added ",(0,o.kt)("inlineCode",{parentName:"p"},"Multiply for")," indicating we're now implementing a trait. Since our function signature already matched the trait's expected signature, we are done. If we had previously called out one-off method something different like \"times\" or something, we would have to make minor adjustments there as well."),(0,o.kt)("h2",{id:"limitation"},"Limitation"),(0,o.kt)("p",null,"As a final note, I'll mention one property of Rust's type system that sometimes feels like a limitation. You can only implement a trait for a type if ",(0,o.kt)("em",{parentName:"p"},"either")," the trait or the type (or both) are defined in the same crate as the implementation. While this can sometimes feel like a limitation, it is actually very important to prohibit this. If we allowed implementing Foreign traits on Foreign types, then it may be that multiple conflicting implementation exist in different crates, and the compiler would be unable to catch this because it checks one crate at a time."),(0,o.kt)("p",null,"It is unlikely that you'll encounter this limitation until you have a little more rust experience under your belt. But when you do encounter it, know that there is a work around, and it is to use the new type pattern that we discussed previously, and then implement the trait on your new type."),(0,o.kt)("h1",{id:"some-common-traits-and-collections"},"Some Common Traits and Collections"),(0,o.kt)("p",null,"In this module we have discussed how to create your own types, both structs and enums, create your own traits for abstracting shared behavior, and implement your traits on your types. In this final video, we'll take a look through some of the most common types and traits that are defined in the standard library and how you might encounter them."),(0,o.kt)("p",null,"TODO flesh this out."),(0,o.kt)("h2",{id:"types"},"Types"),(0,o.kt)("p",null,"Vec\nString\nBTreeMap\nBTreeSet - Note there are also hashtable based maps and sets with similar interfaces, but we will not be looking at them because they are not available in the Substrate Runtime."),(0,o.kt)("h2",{id:"traits-1"},"Traits"),(0,o.kt)("p",null,"Iterator\nDisplay\nDebug\nDefault"))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e159a34b.3f058361.js b/assets/js/e159a34b.3f058361.js new file mode 100644 index 000000000..1dd9b7e21 --- /dev/null +++ b/assets/js/e159a34b.3f058361.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[30],{3905:(e,t,o)=>{o.d(t,{Zo:()=>c,kt:()=>m});var n=o(7294);function r(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function a(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function i(e){for(var t=1;t=0||(r[o]=e[o]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(r[o]=e[o])}return r}var l=n.createContext({}),d=function(e){var t=n.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):i(i({},t),e)),o},c=function(e){var t=d(e.components);return n.createElement(l.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var o=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=d(o),h=r,m=p["".concat(l,".").concat(h)]||p[h]||u[h]||a;return o?n.createElement(m,i(i({ref:t},c),{},{components:o})):n.createElement(m,i({ref:t},c))}));function m(e,t){var o=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=o.length,i=new Array(a);i[0]=h;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:r,i[1]=s;for(var d=2;d{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>d});var n=o(7462),r=(o(7294),o(3905));const a={id:"networking"},i="Networking",s={unversionedId:"Polkadot/Module4/networking",id:"Polkadot/Module4/networking",title:"Networking",description:"All validators have their own local clock and their clocks do",source:"@site/docs/Polkadot/Module4/networking.md",sourceDirName:"Polkadot/Module4",slug:"/Polkadot/Module4/networking",permalink:"/docs/Polkadot/Module4/networking",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module4/networking.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"networking"},sidebar:"polkadot",previous:{title:"Cryptography",permalink:"/docs/Polkadot/Module4/cryptography"},next:{title:"Nodes on Polkadot Network",permalink:"/docs/Polkadot/Module4/nodes"}},l={},d=[{value:"Gossipping",id:"gossipping",level:2},{value:"Networking Complexity",id:"networking-complexity",level:2}],c={toc:d},p="wrapper";function u(e){let{components:t,...o}=e;return(0,r.kt)(p,(0,n.Z)({},c,o,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"networking"},"Networking"),(0,r.kt)("p",null,"All validators have their own local clock and their clocks do\nnot rely on any central clock. We assume that validators and collators are in a partially synchronous\nnetwork. It means that a message sent by a validator or a collator arrives at all parties in the\nnetwork at most ",(0,r.kt)("em",{parentName:"p"},"d")," units of time later where ",(0,r.kt)("em",{parentName:"p"},"d")," is an unknown parameter. So, we assume an\neventual delivery of a message in Polkadot."),(0,r.kt)("p",null,"Polkadot's networking needs to extend the peer-to-peer gossip network that is standard in single chain\npermissionless blockchains to a multi-chain system, where any nodes network traffic should not scale\nwith the total data of the system."),(0,r.kt)("h2",{id:"gossipping"},"Gossipping"),(0,r.kt)("p",null,"This subprotocol is used for most relay-chain artefacts, where everyone needs to see more-or-less\nthe same public information. Part of its structure is also used for when a node goes offline for a\nlong time and needs to synchronise any newer data it hasn't seen before.\nThe Polkadot relay chain network forms a gossip overlay network on top of the physical commu-\nnications network, as an efficient way to provide a decentralised broadcast medium. The network\nconsists of a known number of trusted nodes (validators) who have been permissioned via staking,\nand an unknown number of untrusted nodes (full nodes that don't perform validation) from the\npermissionless open internet. (As an aside, recall that some of the untrusted nodes may have other\nroles as defined earlier, e.g. parachain collator, fishermen, etc.)\nA simple push-based approach is implemented currently, with hash-based tracker caches to\navoid sending duplicates to peers, and a few restrictions to avoid the most common spam attacks:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"Artefacts may only be received in dependency order; peers are not allowed to send them out-\nof-order. Though this decreases network-level efficiency, it is straightforward to implement\nand provides a healthy level of security.")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"To efficiently communicate to sending peers what they are allowed to send in dependency\norder, periodically peers update each other with their view of the latest heads of the chain."))),(0,r.kt)("p",null,"There are also more specific constraint rules applied to artefacts belonging to the various higher-\nlevel subprotocols using the gossip protocol, to avoid broadcasting obsolete or otherwise unneeded\nartefacts. For example, for GRANDPA we only allow two votes being received for each type of\nvote, round number, and voter; any further votes will be ignored. For block production only valid\nblock producers are allowed to produce one block per round; any further blocks will be ignored.\nThere is basic support for sentry nodes, proxy servers that are essentially the only neighbour\nof a private server, running more security-critical operations such the validator role.\nThe network topology is a weak point currently; nodes connect to each other on an ad-hoc\nbasis by performing random lookups in the address book. Further work will proceed along two\nfronts:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Trusted nodes will reserve a portion of their bandwidth and connection resources, to form\na structured overlay with a deterministic but unpredictable topology that rotates every era.\nFor nodes running behind sentries, this effectively means that their sentry nodes instead\nparticipate in this topology."),(0,r.kt)("li",{parentName:"ol"},"For the remainder of trusted nodes' resource capacity, and for the whole of untrusted nodes'\nresource capacity, they will select neighbours via a scheme based on latency measurements,\nwith the details to be decided. Notably, for good security properties we want a scheme that\ndoes not simply choose \"closest first\", but also some far links as well.\nIn some sense, this can be viewed as the trusted nodes forming a core with the untrusted\nnodes around it - but note that trusted nodes are expected to use some of their resources to serve\nuntrusted nodes as well. Both topologies are chosen to mitigate eclipse attacks, as well as sybil\nattacks in the permissionless untrusted case.\nFurther work will also include some sort of set reconciliation protocol, to further reduce re-\ndundancy when many senders attempt to send the same object to the same recipient at once; and\npotentially look into lifting the dependency-order restriction whilst retaining security.")),(0,r.kt)("h2",{id:"networking-complexity"},"Networking Complexity"),(0,r.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/yvlUBL1GNdk",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e159a34b.bc159c19.js b/assets/js/e159a34b.bc159c19.js deleted file mode 100644 index 2f1cd40bd..000000000 --- a/assets/js/e159a34b.bc159c19.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[30],{3905:(e,t,o)=>{o.d(t,{Zo:()=>c,kt:()=>m});var n=o(7294);function r(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function a(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function s(e){for(var t=1;t=0||(r[o]=e[o]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(r[o]=e[o])}return r}var l=n.createContext({}),d=function(e){var t=n.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):s(s({},t),e)),o},c=function(e){var t=d(e.components);return n.createElement(l.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var o=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),p=d(o),h=r,m=p["".concat(l,".").concat(h)]||p[h]||u[h]||a;return o?n.createElement(m,s(s({ref:t},c),{},{components:o})):n.createElement(m,s({ref:t},c))}));function m(e,t){var o=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=o.length,s=new Array(a);s[0]=h;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i[p]="string"==typeof e?e:r,s[1]=i;for(var d=2;d{o.r(t),o.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>u,frontMatter:()=>a,metadata:()=>i,toc:()=>d});var n=o(7462),r=(o(7294),o(3905));const a={id:"networking"},s="Networking",i={unversionedId:"Polkadot/Module4/networking",id:"Polkadot/Module4/networking",title:"Networking",description:"All validators have their own local clock and their clocks do",source:"@site/docs/Polkadot/Module4/networking.md",sourceDirName:"Polkadot/Module4",slug:"/Polkadot/Module4/networking",permalink:"/docs/Polkadot/Module4/networking",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module4/networking.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1654881714,formattedLastUpdatedAt:"Jun 10, 2022",frontMatter:{id:"networking"},sidebar:"polkadot",previous:{title:"Cryptography",permalink:"/docs/Polkadot/Module4/cryptography"},next:{title:"Nodes on Polkadot Network",permalink:"/docs/Polkadot/Module4/nodes"}},l={},d=[{value:"Gossipping",id:"gossipping",level:2},{value:"Networking Complexity",id:"networking-complexity",level:2}],c={toc:d},p="wrapper";function u(e){let{components:t,...o}=e;return(0,r.kt)(p,(0,n.Z)({},c,o,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"networking"},"Networking"),(0,r.kt)("p",null,"All validators have their own local clock and their clocks do\nnot rely on any central clock. We assume that validators and collators are in a partially synchronous\nnetwork. It means that a message sent by a validator or a collator arrives at all parties in the\nnetwork at most ",(0,r.kt)("em",{parentName:"p"},"d")," units of time later where ",(0,r.kt)("em",{parentName:"p"},"d")," is an unknown parameter. So, we assume an\neventual delivery of a message in Polkadot."),(0,r.kt)("p",null,"Polkadot's networking needs to extend the peer-to-peer gossip network that is standard in single chain\npermissionless blockchains to a multi-chain system, where any nodes network traffic should not scale\nwith the total data of the system."),(0,r.kt)("h2",{id:"gossipping"},"Gossipping"),(0,r.kt)("p",null,"This subprotocol is used for most relay-chain artefacts, where everyone needs to see more-or-less\nthe same public information. Part of its structure is also used for when a node goes o\u2000ine for a\nlong time and needs to synchronise any newer data it hasn't seen before.\nThe Polkadot relay chain network forms a gossip overlay network on top of the physical commu-\nnications network, as an e\u2000cient way to provide a decentralised broadcast medium. The network\nconsists of a known number of trusted nodes (validators) who have been permissioned via staking,\nand an unknown number of untrusted nodes (full nodes that don't perform validation) from the\npermissionless open internet. (As an aside, recall that some of the untrusted nodes may have other\nroles as de\u2000ned earlier, e.g. parachain collator, \u2000shermen, etc.)\nA simple push-based approach is implemented currently, with hash-based tracker caches to\navoid sending duplicates to peers, and a few restrictions to avoid the most common spam attacks:\n\u2000 Artefacts may only be received in dependency order; peers are not allowed to send them out-\nof-order. Though this decreases network-level e\u2000ciency, it is straightforward to implement\nand provides a healthy level of security."),(0,r.kt)("p",null,"\u2000 To e\u2000ciently communicate to sending peers what they are allowed to send in dependency\norder, periodically peers update each other with their view of the latest heads of the chain.\nThere are also more speci\u2000c constraint rules applied to artefacts belonging to the various higher-\nlevel subprotocols using the gossip protocol, to avoid broadcasting obsolete or otherwise unneeded\nartefacts. For example, for GRANDPA we only allow two votes being received for each type of\nvote, round number, and voter; any further votes will be ignored. For block production only valid\nblock producers are allowed to produce one block per round; any further blocks will be ignored.\nThere is basic support for sentry nodes, proxy servers that are essentially the only neighbour\nof a private server, running more security-critical operations such the validator role.\nThe network topology is a weak point currently; nodes connect to each other on an ad-hoc\nbasis by performing random lookups in the address book. Further work will proceed along two\nfronts:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Trusted nodes will reserve a portion of their bandwidth and connection resources, to form\na structured overlay with a deterministic but unpredictable topology that rotates every era.\nFor nodes running behind sentries, this e\u2000ectively means that their sentry nodes instead\nparticipate in this topology."),(0,r.kt)("li",{parentName:"ol"},"For the remainder of trusted nodes' resource capacity, and for the whole of untrusted nodes'\nresource capacity, they will select neighbours via a scheme based on latency measurements,\nwith the details to be decided. Notably, for good security properties we want a scheme that\ndoes not simply choose \"closest \u2000rst\", but also some far links as well.\nIn some sense, this can be viewed as the trusted nodes forming a core with the untrusted\nnodes around it - but note that trusted nodes are expected to use some of their resources to serve\nuntrusted nodes as well. Both topologies are chosen to mitigate eclipse attacks, as well as sybil\nattacks in the permissionless untrusted case.\nFurther work will also include some sort of set reconciliation protocol, to further reduce re-\ndundancy when many senders attempt to send the same object to the same recipient at once; and\npotentially look into lifting the dependency-order restriction whilst retaining security.")),(0,r.kt)("h2",{id:"networking-complexity"},"Networking Complexity"),(0,r.kt)("iframe",{width:"560",height:"315",src:"https://www.youtube.com/embed/yvlUBL1GNdk",title:"YouTube video player",frameborder:"0",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowfullscreen:!0}))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fe76b6c3.572fe957.js b/assets/js/fe76b6c3.572fe957.js new file mode 100644 index 000000000..ed7d61983 --- /dev/null +++ b/assets/js/fe76b6c3.572fe957.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[9125],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>y});var n=a(7294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},d=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},b=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),h=c(a),b=i,y=h["".concat(s,".").concat(b)]||h[b]||u[b]||o;return a?n.createElement(y,r(r({ref:t},d),{},{components:a})):n.createElement(y,r({ref:t},d))}));function y(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=a.length,r=new Array(o);r[0]=b;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[h]="string"==typeof e?e:i,r[1]=l;for(var c=2;c{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var n=a(7462),i=(a(7294),a(3905));const o={id:"scalability"},r="Scalability",l={unversionedId:"Polkadot/Module5/scalability",id:"Polkadot/Module5/scalability",title:"Scalability",description:"Validity and Availability",source:"@site/docs/Polkadot/Module5/scalability.md",sourceDirName:"Polkadot/Module5",slug:"/Polkadot/Module5/scalability",permalink:"/docs/Polkadot/Module5/scalability",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module5/scalability.md",tags:[],version:"current",lastUpdatedBy:"D\xf3nal Murray",lastUpdatedAt:1692614373,formattedLastUpdatedAt:"Aug 21, 2023",frontMatter:{id:"scalability"},sidebar:"polkadot",previous:{title:"Parachains",permalink:"/docs/Polkadot/Module5/parachains"},next:{title:"Polkadot Architecture Improvements",permalink:"/docs/Polkadot/Module5/architectureimprovements"}},s={},c=[{value:"Validity and Availability",id:"validity-and-availability",level:2}],d={toc:c},h="wrapper";function u(e){let{components:t,...a}=e;return(0,i.kt)(h,(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"scalability"},"Scalability"),(0,i.kt)("h2",{id:"validity-and-availability"},"Validity and Availability"),(0,i.kt)("p",null,"Once a parachain block is created it is important that the parachain blob consisting of the PoV\nblock and set of outgoing messages from the parachain is available for a while. The naive solution\nfor this would be broadcasting/gossip the parachain blobs to all relay chain nodes, which is not\na feasible option because there are many parachains and the PoV blocks may be big. We want\nto find an efficient solution to ensure PoV blocks from any recently created parachain blocks are\navailable."),(0,i.kt)("p",null,"For a single chain, such as Bitcoin, as long as 51% of hash power is honest, not making block data\navailable ensures that no honest miner builds on it so it will not be in the final chain. However,\nparachain consensus in Polkadot is determined by relay chain consensus. A parachain block is\ncanonical when its header is in the relay chain. We have no guarantees that anyone other than the\ncollator and parachain validators have seen the PoV block. If these collude then the rest of the\nparachain network need not have the parachain block and then most collators cannot build a new\nblock and this block's invalidity may not be discovered. We would like the consensus participants,\nhere the validators, to collectively guarantee the availability rather than relying on a few nodes.\nTo this end we designed an availability scheme that uses erasure coding to\ndistribute the PoV block to all validators. When any misbehaviour, particularly in relation to\ninvalidity, is detected, the blob can be reconstructed from the distributed erasure coded pieces.\nIf a block is available then full nodes of the parachain, and any light client that has the PoV\nblock, can check its validity. We have three-level of validity checks in Polkadot. The first validity\ncheck of a PoV block is executed by the corresponding parachain validators. If they verify the\nPoV block then they sign and distribute the erasure codes of the blob, including the PoV block, to\neach validator. We rely on nodes acting as fishermen to report the invalidity of a blob as a second\nlevel of validity checking. They would need to back any claim with their own stake in DOTs. We\nwould assume that most collators will be fishermen, as they have a stake in continued validity of\nthe chain and are already running full nodes, so all they need is stake in DOTs. The third level of\nvalidity checking is executed by a few randomly and privately assigned validators. We determine\nthe number of validators in the third level of validity checking considering the amount of invalidity\nreports given by fishermen and unavailability reports given by collators. If an invalid parachain\nblock is detected, the validators who signed for its validity are slashed. We wait for enough of\nthese randomly assigned checkers to check the block before voting on it in GRANDPA. We also\nwant to ensure that the block is available before selecting the randomly assigned validators. This\nmeans that the parachain validators have to commit running a high risk of being slashed for a small\nprobability of getting an invalid block finalised. This means that the expected cost of getting an\ninvalid block into Polkadot is higher than the amount of stake backing a single parachain.\nThe security of our availability and validity scheme is based on the security of the GRANDPA\nfinality gadget and the quality of randomness generated in each BABE epoch."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fe76b6c3.5f90ddbd.js b/assets/js/fe76b6c3.5f90ddbd.js deleted file mode 100644 index 98987f943..000000000 --- a/assets/js/fe76b6c3.5f90ddbd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[9125],{3905:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>y});var n=a(7294);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},d=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},b=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),h=c(a),b=i,y=h["".concat(s,".").concat(b)]||h[b]||u[b]||o;return a?n.createElement(y,r(r({ref:t},d),{},{components:a})):n.createElement(y,r({ref:t},d))}));function y(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=a.length,r=new Array(o);r[0]=b;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[h]="string"==typeof e?e:i,r[1]=l;for(var c=2;c{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var n=a(7462),i=(a(7294),a(3905));const o={id:"scalability"},r="Scalability",l={unversionedId:"Polkadot/Module5/scalability",id:"Polkadot/Module5/scalability",title:"Scalability",description:"Validity and Availability",source:"@site/docs/Polkadot/Module5/scalability.md",sourceDirName:"Polkadot/Module5",slug:"/Polkadot/Module5/scalability",permalink:"/docs/Polkadot/Module5/scalability",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Polkadot/Module5/scalability.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1654881714,formattedLastUpdatedAt:"Jun 10, 2022",frontMatter:{id:"scalability"},sidebar:"polkadot",previous:{title:"Parachains",permalink:"/docs/Polkadot/Module5/parachains"},next:{title:"Polkadot Architecture Improvements",permalink:"/docs/Polkadot/Module5/architectureimprovements"}},s={},c=[{value:"Validity and Availability",id:"validity-and-availability",level:2}],d={toc:c},h="wrapper";function u(e){let{components:t,...a}=e;return(0,i.kt)(h,(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"scalability"},"Scalability"),(0,i.kt)("h2",{id:"validity-and-availability"},"Validity and Availability"),(0,i.kt)("p",null,"Once a parachain block is created it is important that the parachain blob consisting of the PoV\nblock and set of outgoing messages from the parachain is available for a while. The naive solution\nfor this would be broadcasting/gossip the parachain blobs to all relay chain nodes, which is not\na feasible option because there are many parachains and the PoV blocks may be big. We want\nto \u2000nd an e\u2000cient solution to ensure PoV blocks from any recently created parachain blocks are\navailable."),(0,i.kt)("p",null,"For a single chain, such as Bitcoin, as long as 51% of hash power is honest, not making block data\navailable ensures that no honest miner builds on it so it will not be in the \u2000nal chain. However,\nparachain consensus in Polkadot is determined by relay chain consensus. A parachain block is\ncanonical when its header is in the relay chain. We have no guarantees that anyone other than the\ncollator and parachain validators have seen the PoV block. If these collude then the rest of the\nparachain network need not have the parachain block and then most collators cannot build a new\nblock and this block's invalidity may not be discovered. We would like the consensus participants,\nhere the validators, to collectively guarantee the availability rather than relying on a few nodes.\nTo this end we designed an availability scheme that uses erasure coding to\ndistribute the PoV block to all validators. When any misbehaviour, particularly in relation to\ninvalidity, is detected, the blob can be reconstructed from the distributed erasure coded pieces.\nIf a block is available then full nodes of the parachain, and any light client that has the PoV\nblock, can check its validity. We have three-level of validity checks in Polkadot. The \u2000rst validity\ncheck of a PoV block is executed by the corresponding parachain validators. If they verify the\nPoV block then they sign and distribute the erasure codes of the blob, including the PoV block, to\neach validator. We rely on nodes acting as \u2000shermen to report the invalidity of a blob as a second\nlevel of validity checking. They would need to back any claim with their own stake in DOTs. We\nwould assume that most collators will be \u2000shermen, as they have a stake in continued validity of\nthe chain and are already running full nodes, so all they need is stake in DOTs. The third level of\nvalidity checking is executed by a few randomly and privately assigned validators. We determine\nthe number of validators in the third level of validity checking considering the amount of invalidity\nreports given by \u2000shermen and unavailability reports given by collators. If an invalid parachain\nblock is detected, the validators who signed for its validity are slashed. We wait for enough of\nthese randomly assigned checkers to check the block before voting on it in GRANDPA. We also\nwant to ensure that the block is available before selecting the randomly assigned validators. This\nmeans that the parachain validators have to commit running a high risk of being slashed for a small\nprobability of getting an invalid block \u2000nalised. This means that the expected cost of getting an\ninvalid block into Polkadot is higher than the amount of stake backing a single parachain.\nThe security of our availability and validity scheme is based on the security of the GRANDPA\n\u2000nality gadget and the quality of randomness generated in each BABE epoch."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.c47f1043.js b/assets/js/runtime~main.dc484ca7.js similarity index 92% rename from assets/js/runtime~main.c47f1043.js rename to assets/js/runtime~main.dc484ca7.js index 289b23425..6d8bf8067 100644 --- a/assets/js/runtime~main.c47f1043.js +++ b/assets/js/runtime~main.dc484ca7.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,b,d,f,c={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var b=t[e]={exports:{}};return c[e].call(b.exports,b,b.exports,r),b.exports}r.m=c,e=[],r.O=(a,b,d,f)=>{if(!b){var c=1/0;for(i=0;i=f)&&Object.keys(r.O).every((e=>r.O[e](b[o])))?b.splice(o--,1):(t=!1,f0&&e[i-1][2]>f;i--)e[i]=e[i-1];e[i]=[b,d,f]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},b=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,d){if(1&d&&(e=this(e)),8&d)return e;if("object"==typeof e&&e){if(4&d&&e.__esModule)return e;if(16&d&&"function"==typeof e.then)return e}var f=Object.create(null);r.r(f);var c={};a=a||[null,b({}),b([]),b(b)];for(var t=2&d&&e;"object"==typeof t&&!~a.indexOf(t);t=b(t))Object.getOwnPropertyNames(t).forEach((a=>c[a]=()=>e[a]));return c.default=()=>e,r.d(f,c),f},r.d=(e,a)=>{for(var b in a)r.o(a,b)&&!r.o(e,b)&&Object.defineProperty(e,b,{enumerable:!0,get:a[b]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,b)=>(r.f[b](e,a),a)),[])),r.u=e=>"assets/js/"+({30:"e159a34b",45:"b6e1accc",53:"935f2afb",79:"bf99f0d1",162:"dba49971",166:"a70cf35a",240:"bb3b9c27",292:"697abe16",437:"2a3afb9a",533:"b2b675dd",572:"f732ab87",644:"79092693",682:"ac0fbae6",724:"c23220d2",898:"e36ee660",901:"47521d56",927:"a7071214",961:"0ec98211",1004:"66f9d3f7",1121:"8a4ddfdc",1158:"51a84af5",1209:"6e12f270",1244:"8eae764b",1309:"0ee51ef9",1413:"0b931a33",1477:"b2f554cd",1516:"845bb328",1554:"1fd9a240",1578:"5cbab3ba",1621:"b5a07b06",1652:"20b9e0fb",1713:"a7023ddc",1760:"d630c86a",1801:"fa73f8cd",1811:"9af3c048",1818:"bf8854c0",1884:"38aea4d2",1954:"4d534538",1969:"e142862f",2054:"1b86bc4d",2064:"12836233",2156:"17abfe6e",2157:"1ecd3227",2275:"912dd27b",2323:"7e18d183",2362:"60080a18",2365:"be4f7283",2395:"cc3e0997",2409:"8d13930c",2481:"778bb0ff",2489:"18b60c58",2501:"9878ab02",2535:"814f3328",2567:"125052bb",2594:"89daa71f",2623:"e23acd1c",2625:"7ab8a3b9",2640:"b818a5fc",2667:"3f74b182",2782:"b0a340c1",2944:"d9ff3f6f",2975:"6baa9e40",2991:"bf0acaf4",3054:"04ff3f93",3089:"a6aa9e1f",3109:"56775098",3122:"3a78b9a2",3149:"860ac776",3153:"cdf6bb5b",3160:"8033be4e",3298:"142a84ed",3347:"698a20df",3368:"bed02607",3369:"14263310",3393:"9eca237c",3469:"0360b8e0",3471:"30d72593",3498:"85fcf0f9",3608:"9e4087bc",3671:"64e95fdc",3708:"55a851e8",3880:"4e20c90e",4013:"01a85c17",4033:"e3b93dc1",4050:"ec1fcf37",4054:"15037438",4063:"47047f6c",4071:"98813d19",4072:"a94eb289",4080:"dd33d834",4083:"1c093ed7",4192:"90dc3e28",4195:"c4f5d8e4",4278:"748750de",4454:"e66cda10",4509:"5f14d8ef",4539:"996ac392",4581:"89914d9a",4656:"4eee41d2",4662:"cfb4c858",4690:"ebc08f42",4738:"6f272f6d",4799:"e3852037",4937:"8a7c7b85",4993:"c41cffcf",5088:"7f498ece",5123:"c0a37a65",5137:"3878690d",5170:"c20d2866",5193:"75a26373",5215:"819e4c7d",5221:"36c7c7bf",5225:"d08ea62b",5276:"92a5cba8",5338:"d698cec0",5372:"df1189de",5465:"91599b81",5466:"f3700f36",5502:"ed7206fb",5599:"093f602f",5605:"bddd9d63",5644:"b1304a34",5658:"d6125dd8",5691:"10db19bd",5701:"4eeb835a",5738:"98650093",5864:"57376d8e",5905:"bdab9abd",5963:"611d03fc",5979:"dc107bf9",5980:"10b9b081",6103:"ccc49370",6175:"4a418313",6207:"a9f2f82e",6346:"fa1554a2",6497:"14bc938c",6502:"eedbd05c",6561:"85386164",6573:"f58a59cf",6586:"3cf1419d",6611:"650207e7",6792:"978caf70",6811:"5f5fedc0",6835:"ebaef5a3",6845:"58d818ae",6880:"22fea86a",6881:"939ada28",6979:"86710306",7024:"91bff8d5",7030:"acdb5c5d",7177:"5082a2db",7232:"6e1acb83",7313:"8d4aaee0",7370:"acb31fa2",7419:"a3b1e8a8",7431:"e74f0dea",7451:"5443c55a",7510:"d3564685",7523:"774d3ebf",7595:"9b6998de",7713:"b42777c1",7756:"8980ecfe",7802:"62695a46",7819:"abaf003d",7832:"e4beb833",7859:"3928d779",7918:"17896441",8110:"80bfa7c8",8163:"67f40c53",8187:"821660a9",8188:"d755f20e",8224:"b2def28e",8341:"4b1e850b",8358:"d44414ec",8431:"6743025c",8446:"c0f66a5e",8520:"4ea28616",8527:"1a7a00e4",8610:"6875c492",8675:"e25d2cc3",8718:"21c206d8",8764:"881c1402",8779:"59b286ba",8843:"4e7b4d15",8845:"ddd8c84d",8853:"f4d28708",8994:"5cad4cb9",9003:"f5be66f4",9049:"bb04187e",9088:"61608705",9111:"fb80b757",9125:"fe76b6c3",9168:"4a8e9470",9200:"0939dfa6",9254:"69e7bd09",9356:"37e718a0",9479:"08751a86",9481:"203cb511",9513:"4038b045",9514:"1be78505",9529:"8a655e4b",9594:"8568807f",9616:"e23e66c8",9671:"0e384e19",9674:"db9431d8",9695:"0aa99fa2",9713:"d651ce3a",9844:"f78c3b24",9879:"10533d3e",9885:"1233298f"}[e]||e)+"."+{30:"bc159c19",45:"df2f4449",53:"88f15116",79:"23b243c0",162:"7d0e04c4",166:"230518d5",240:"3e14b03e",292:"96733acd",437:"10355f85",533:"b9a79106",572:"ca19872f",644:"f008f3c4",682:"cee1ae93",724:"4c3e973f",898:"83f55077",901:"6e991101",927:"3f1c5cec",961:"d98cd4fb",1004:"5377e133",1121:"029fdf5c",1158:"ab1ac3a8",1209:"c2442da1",1244:"85d4d03a",1309:"5b649a10",1413:"871b6529",1477:"738cd221",1516:"d9ddd61b",1554:"0f3cb241",1578:"416ae14a",1621:"ae366e5c",1652:"3a72cd3f",1713:"45e17ec4",1760:"b2d6ee12",1801:"2464fb1a",1811:"5d0c931c",1818:"384461c2",1884:"21b7a9c0",1954:"c8469cef",1969:"cb494d2f",2054:"76722694",2064:"f9d71126",2156:"a55d7fd9",2157:"e79c45f9",2275:"0592d153",2323:"f7b3cfa6",2362:"21394205",2365:"0e25a72d",2395:"a6928bfa",2409:"fd964227",2481:"e6a40e1f",2489:"a499fd1a",2501:"be301c2c",2535:"714d131a",2567:"979e1acc",2594:"007a5dcf",2623:"bb7d6606",2625:"4820af3c",2640:"218089fc",2667:"14471590",2782:"586ba27f",2944:"67ff8205",2975:"36938d4d",2991:"5508e2ac",3054:"d06e53ba",3089:"c7e53bef",3109:"2d8e5a36",3122:"9b1f93b0",3149:"9fc1308e",3153:"5052ddc8",3160:"88ab90d0",3298:"c11e8b5e",3347:"108fd7b0",3368:"36b3fd88",3369:"68ffe623",3393:"7a60832e",3469:"8b6f670f",3471:"2b1600c1",3498:"2931718b",3608:"8a9335de",3671:"0ab3deb1",3708:"50ec5685",3880:"054dea55",4013:"ec00a2b1",4033:"584766a3",4050:"7a01ccde",4054:"5c6fd720",4063:"59ffa650",4071:"0503952e",4072:"75a93e06",4080:"ec5123a0",4083:"57c2517e",4192:"9fbbe61d",4195:"53da519e",4278:"cc3bb9f2",4454:"e7c516ee",4509:"4b6f4e1f",4539:"6bf079d7",4581:"e993335d",4656:"560aeec4",4662:"f6a2d74f",4690:"120c32d6",4738:"0c9c869d",4799:"a250a35c",4937:"77aabbde",4972:"d3a97153",4993:"698b5d1a",5088:"25553683",5123:"5373e5ab",5137:"a77a9104",5170:"b087f47e",5193:"bc0b5208",5215:"9f468080",5221:"770def6a",5225:"f1220b02",5276:"b588e433",5338:"dc4f8022",5372:"210abec6",5465:"0711bfa1",5466:"e7af8c5b",5502:"640c35a7",5599:"4093fe70",5605:"e70a84e8",5644:"dfd6d9f4",5658:"c0238936",5691:"e840285e",5701:"71837a23",5738:"d8598f6f",5864:"96be6b6f",5905:"e78c6961",5963:"3605b419",5979:"e84e6c54",5980:"561f8aa2",6048:"91f23ec5",6103:"6e629d0d",6175:"d6dba999",6207:"ee2ab886",6316:"1e6f0db3",6346:"c2b1aa77",6497:"f6925244",6502:"dcca1205",6561:"04e37665",6573:"846f5dd9",6586:"22244bbb",6611:"42b5547e",6792:"97cd7743",6811:"0a820794",6835:"84fe7cd7",6845:"179b6e00",6880:"f59d9bd2",6881:"d73ab82e",6979:"b81cdf4f",7024:"32b5ce12",7030:"591c92fd",7177:"3d222cfd",7232:"c5edba44",7313:"97421c3c",7370:"6c8ebb26",7419:"70783722",7431:"d4891614",7451:"79eb635d",7510:"ef21911a",7523:"a50fa31e",7595:"0566c907",7713:"6f8adb30",7724:"a682bf83",7756:"6ef8853e",7802:"8bad7040",7819:"18b19b89",7832:"d06ae912",7859:"2c63b326",7918:"da7860c7",8110:"67b445f8",8163:"9bb83661",8187:"15408acc",8188:"744b8f14",8224:"35cc8a82",8341:"9c39ccf0",8358:"4567adf4",8431:"664ba346",8446:"3afdb8fc",8520:"9d823df7",8527:"7a32ddc4",8610:"b052f9ac",8675:"bfccbf6c",8718:"1f3949ec",8764:"5b79616f",8779:"6f17d3c6",8843:"b6c101ac",8845:"73e2251a",8853:"1722ffbc",8954:"a967f9d5",8994:"b9209919",9003:"8d55be7e",9049:"a6b0d0b3",9088:"1e6f46ff",9111:"0c2ba893",9125:"5f90ddbd",9168:"ecb0feda",9200:"ddb62803",9254:"894409da",9356:"057dfa1b",9479:"15ca8855",9481:"252302bf",9487:"868c3b2a",9513:"d4633977",9514:"5b96b506",9529:"a7291737",9594:"0034759d",9616:"bdf60722",9671:"ae157a10",9674:"dfe8f48d",9695:"0f3e17a7",9713:"2821960d",9844:"2e6e9ee4",9879:"e7e646d3",9885:"c3b27a40"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),d={},f="w3f-education:",r.l=(e,a,b,c)=>{if(d[e])d[e].push(a);else{var t,o;if(void 0!==b)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var f=d[e];if(delete d[e],t.parentNode&&t.parentNode.removeChild(t),f&&f.forEach((e=>e(b))),a)return a(b)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={12836233:"2064",14263310:"3369",15037438:"4054",17896441:"7918",56775098:"3109",61608705:"9088",79092693:"644",85386164:"6561",86710306:"6979",98650093:"5738",e159a34b:"30",b6e1accc:"45","935f2afb":"53",bf99f0d1:"79",dba49971:"162",a70cf35a:"166",bb3b9c27:"240","697abe16":"292","2a3afb9a":"437",b2b675dd:"533",f732ab87:"572",ac0fbae6:"682",c23220d2:"724",e36ee660:"898","47521d56":"901",a7071214:"927","0ec98211":"961","66f9d3f7":"1004","8a4ddfdc":"1121","51a84af5":"1158","6e12f270":"1209","8eae764b":"1244","0ee51ef9":"1309","0b931a33":"1413",b2f554cd:"1477","845bb328":"1516","1fd9a240":"1554","5cbab3ba":"1578",b5a07b06:"1621","20b9e0fb":"1652",a7023ddc:"1713",d630c86a:"1760",fa73f8cd:"1801","9af3c048":"1811",bf8854c0:"1818","38aea4d2":"1884","4d534538":"1954",e142862f:"1969","1b86bc4d":"2054","17abfe6e":"2156","1ecd3227":"2157","912dd27b":"2275","7e18d183":"2323","60080a18":"2362",be4f7283:"2365",cc3e0997:"2395","8d13930c":"2409","778bb0ff":"2481","18b60c58":"2489","9878ab02":"2501","814f3328":"2535","125052bb":"2567","89daa71f":"2594",e23acd1c:"2623","7ab8a3b9":"2625",b818a5fc:"2640","3f74b182":"2667",b0a340c1:"2782",d9ff3f6f:"2944","6baa9e40":"2975",bf0acaf4:"2991","04ff3f93":"3054",a6aa9e1f:"3089","3a78b9a2":"3122","860ac776":"3149",cdf6bb5b:"3153","8033be4e":"3160","142a84ed":"3298","698a20df":"3347",bed02607:"3368","9eca237c":"3393","0360b8e0":"3469","30d72593":"3471","85fcf0f9":"3498","9e4087bc":"3608","64e95fdc":"3671","55a851e8":"3708","4e20c90e":"3880","01a85c17":"4013",e3b93dc1:"4033",ec1fcf37:"4050","47047f6c":"4063","98813d19":"4071",a94eb289:"4072",dd33d834:"4080","1c093ed7":"4083","90dc3e28":"4192",c4f5d8e4:"4195","748750de":"4278",e66cda10:"4454","5f14d8ef":"4509","996ac392":"4539","89914d9a":"4581","4eee41d2":"4656",cfb4c858:"4662",ebc08f42:"4690","6f272f6d":"4738",e3852037:"4799","8a7c7b85":"4937",c41cffcf:"4993","7f498ece":"5088",c0a37a65:"5123","3878690d":"5137",c20d2866:"5170","75a26373":"5193","819e4c7d":"5215","36c7c7bf":"5221",d08ea62b:"5225","92a5cba8":"5276",d698cec0:"5338",df1189de:"5372","91599b81":"5465",f3700f36:"5466",ed7206fb:"5502","093f602f":"5599",bddd9d63:"5605",b1304a34:"5644",d6125dd8:"5658","10db19bd":"5691","4eeb835a":"5701","57376d8e":"5864",bdab9abd:"5905","611d03fc":"5963",dc107bf9:"5979","10b9b081":"5980",ccc49370:"6103","4a418313":"6175",a9f2f82e:"6207",fa1554a2:"6346","14bc938c":"6497",eedbd05c:"6502",f58a59cf:"6573","3cf1419d":"6586","650207e7":"6611","978caf70":"6792","5f5fedc0":"6811",ebaef5a3:"6835","58d818ae":"6845","22fea86a":"6880","939ada28":"6881","91bff8d5":"7024",acdb5c5d:"7030","5082a2db":"7177","6e1acb83":"7232","8d4aaee0":"7313",acb31fa2:"7370",a3b1e8a8:"7419",e74f0dea:"7431","5443c55a":"7451",d3564685:"7510","774d3ebf":"7523","9b6998de":"7595",b42777c1:"7713","8980ecfe":"7756","62695a46":"7802",abaf003d:"7819",e4beb833:"7832","3928d779":"7859","80bfa7c8":"8110","67f40c53":"8163","821660a9":"8187",d755f20e:"8188",b2def28e:"8224","4b1e850b":"8341",d44414ec:"8358","6743025c":"8431",c0f66a5e:"8446","4ea28616":"8520","1a7a00e4":"8527","6875c492":"8610",e25d2cc3:"8675","21c206d8":"8718","881c1402":"8764","59b286ba":"8779","4e7b4d15":"8843",ddd8c84d:"8845",f4d28708:"8853","5cad4cb9":"8994",f5be66f4:"9003",bb04187e:"9049",fb80b757:"9111",fe76b6c3:"9125","4a8e9470":"9168","0939dfa6":"9200","69e7bd09":"9254","37e718a0":"9356","08751a86":"9479","203cb511":"9481","4038b045":"9513","1be78505":"9514","8a655e4b":"9529","8568807f":"9594",e23e66c8:"9616","0e384e19":"9671",db9431d8:"9674","0aa99fa2":"9695",d651ce3a:"9713",f78c3b24:"9844","10533d3e":"9879","1233298f":"9885"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(a,b)=>{var d=r.o(e,a)?e[a]:void 0;if(0!==d)if(d)b.push(d[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var f=new Promise(((b,f)=>d=e[a]=[b,f]));b.push(d[2]=f);var c=r.p+r.u(a),t=new Error;r.l(c,(b=>{if(r.o(e,a)&&(0!==(d=e[a])&&(e[a]=void 0),d)){var f=b&&("load"===b.type?"missing":b.type),c=b&&b.target&&b.target.src;t.message="Loading chunk "+a+" failed.\n("+f+": "+c+")",t.name="ChunkLoadError",t.type=f,t.request=c,d[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,b)=>{var d,f,c=b[0],t=b[1],o=b[2],n=0;if(c.some((a=>0!==e[a]))){for(d in t)r.o(t,d)&&(r.m[d]=t[d]);if(o)var i=o(r)}for(a&&a(b);n{"use strict";var e,a,b,d,f,c={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var b=t[e]={exports:{}};return c[e].call(b.exports,b,b.exports,r),b.exports}r.m=c,e=[],r.O=(a,b,d,f)=>{if(!b){var c=1/0;for(i=0;i=f)&&Object.keys(r.O).every((e=>r.O[e](b[o])))?b.splice(o--,1):(t=!1,f0&&e[i-1][2]>f;i--)e[i]=e[i-1];e[i]=[b,d,f]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},b=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,d){if(1&d&&(e=this(e)),8&d)return e;if("object"==typeof e&&e){if(4&d&&e.__esModule)return e;if(16&d&&"function"==typeof e.then)return e}var f=Object.create(null);r.r(f);var c={};a=a||[null,b({}),b([]),b(b)];for(var t=2&d&&e;"object"==typeof t&&!~a.indexOf(t);t=b(t))Object.getOwnPropertyNames(t).forEach((a=>c[a]=()=>e[a]));return c.default=()=>e,r.d(f,c),f},r.d=(e,a)=>{for(var b in a)r.o(a,b)&&!r.o(e,b)&&Object.defineProperty(e,b,{enumerable:!0,get:a[b]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,b)=>(r.f[b](e,a),a)),[])),r.u=e=>"assets/js/"+({30:"e159a34b",45:"b6e1accc",53:"935f2afb",79:"bf99f0d1",162:"dba49971",166:"a70cf35a",240:"bb3b9c27",292:"697abe16",437:"2a3afb9a",533:"b2b675dd",572:"f732ab87",644:"79092693",682:"ac0fbae6",724:"c23220d2",898:"e36ee660",901:"47521d56",927:"a7071214",961:"0ec98211",1004:"66f9d3f7",1121:"8a4ddfdc",1158:"51a84af5",1209:"6e12f270",1244:"8eae764b",1309:"0ee51ef9",1413:"0b931a33",1477:"b2f554cd",1516:"845bb328",1554:"1fd9a240",1578:"5cbab3ba",1621:"b5a07b06",1652:"20b9e0fb",1713:"a7023ddc",1760:"d630c86a",1801:"fa73f8cd",1811:"9af3c048",1818:"bf8854c0",1884:"38aea4d2",1954:"4d534538",1969:"e142862f",2054:"1b86bc4d",2064:"12836233",2156:"17abfe6e",2157:"1ecd3227",2275:"912dd27b",2323:"7e18d183",2362:"60080a18",2365:"be4f7283",2395:"cc3e0997",2409:"8d13930c",2481:"778bb0ff",2489:"18b60c58",2501:"9878ab02",2535:"814f3328",2567:"125052bb",2594:"89daa71f",2623:"e23acd1c",2625:"7ab8a3b9",2640:"b818a5fc",2667:"3f74b182",2782:"b0a340c1",2944:"d9ff3f6f",2975:"6baa9e40",2991:"bf0acaf4",3054:"04ff3f93",3089:"a6aa9e1f",3109:"56775098",3122:"3a78b9a2",3149:"860ac776",3153:"cdf6bb5b",3160:"8033be4e",3298:"142a84ed",3347:"698a20df",3368:"bed02607",3369:"14263310",3393:"9eca237c",3469:"0360b8e0",3471:"30d72593",3498:"85fcf0f9",3608:"9e4087bc",3671:"64e95fdc",3708:"55a851e8",3880:"4e20c90e",4013:"01a85c17",4033:"e3b93dc1",4050:"ec1fcf37",4054:"15037438",4063:"47047f6c",4071:"98813d19",4072:"a94eb289",4080:"dd33d834",4083:"1c093ed7",4192:"90dc3e28",4195:"c4f5d8e4",4278:"748750de",4454:"e66cda10",4509:"5f14d8ef",4539:"996ac392",4581:"89914d9a",4656:"4eee41d2",4662:"cfb4c858",4690:"ebc08f42",4738:"6f272f6d",4799:"e3852037",4937:"8a7c7b85",4993:"c41cffcf",5088:"7f498ece",5123:"c0a37a65",5137:"3878690d",5170:"c20d2866",5193:"75a26373",5215:"819e4c7d",5221:"36c7c7bf",5225:"d08ea62b",5276:"92a5cba8",5338:"d698cec0",5372:"df1189de",5465:"91599b81",5466:"f3700f36",5502:"ed7206fb",5599:"093f602f",5605:"bddd9d63",5644:"b1304a34",5658:"d6125dd8",5691:"10db19bd",5701:"4eeb835a",5738:"98650093",5864:"57376d8e",5905:"bdab9abd",5963:"611d03fc",5979:"dc107bf9",5980:"10b9b081",6103:"ccc49370",6175:"4a418313",6207:"a9f2f82e",6346:"fa1554a2",6497:"14bc938c",6502:"eedbd05c",6561:"85386164",6573:"f58a59cf",6586:"3cf1419d",6611:"650207e7",6792:"978caf70",6811:"5f5fedc0",6835:"ebaef5a3",6845:"58d818ae",6880:"22fea86a",6881:"939ada28",6979:"86710306",7024:"91bff8d5",7030:"acdb5c5d",7177:"5082a2db",7232:"6e1acb83",7313:"8d4aaee0",7370:"acb31fa2",7419:"a3b1e8a8",7431:"e74f0dea",7451:"5443c55a",7510:"d3564685",7523:"774d3ebf",7595:"9b6998de",7713:"b42777c1",7756:"8980ecfe",7802:"62695a46",7819:"abaf003d",7832:"e4beb833",7859:"3928d779",7918:"17896441",8110:"80bfa7c8",8163:"67f40c53",8187:"821660a9",8188:"d755f20e",8224:"b2def28e",8341:"4b1e850b",8358:"d44414ec",8431:"6743025c",8446:"c0f66a5e",8520:"4ea28616",8527:"1a7a00e4",8610:"6875c492",8675:"e25d2cc3",8718:"21c206d8",8764:"881c1402",8779:"59b286ba",8843:"4e7b4d15",8845:"ddd8c84d",8853:"f4d28708",8994:"5cad4cb9",9003:"f5be66f4",9049:"bb04187e",9088:"61608705",9111:"fb80b757",9125:"fe76b6c3",9168:"4a8e9470",9200:"0939dfa6",9254:"69e7bd09",9356:"37e718a0",9479:"08751a86",9481:"203cb511",9513:"4038b045",9514:"1be78505",9529:"8a655e4b",9594:"8568807f",9616:"e23e66c8",9671:"0e384e19",9674:"db9431d8",9695:"0aa99fa2",9713:"d651ce3a",9844:"f78c3b24",9879:"10533d3e",9885:"1233298f"}[e]||e)+"."+{30:"3f058361",45:"df2f4449",53:"88f15116",79:"23b243c0",162:"7d0e04c4",166:"230518d5",240:"3e14b03e",292:"96733acd",437:"10355f85",533:"b9a79106",572:"ca19872f",644:"f008f3c4",682:"cee1ae93",724:"4c3e973f",898:"83f55077",901:"ce090131",927:"3f1c5cec",961:"d98cd4fb",1004:"5c664fe1",1121:"029fdf5c",1158:"ab1ac3a8",1209:"c2442da1",1244:"85d4d03a",1309:"5b649a10",1413:"871b6529",1477:"738cd221",1516:"57d0cd9a",1554:"0f3cb241",1578:"416ae14a",1621:"ae366e5c",1652:"3a72cd3f",1713:"45e17ec4",1760:"b2d6ee12",1801:"2464fb1a",1811:"5d0c931c",1818:"384461c2",1884:"21b7a9c0",1954:"c8469cef",1969:"cb494d2f",2054:"76722694",2064:"f9d71126",2156:"a55d7fd9",2157:"e79c45f9",2275:"0592d153",2323:"f7b3cfa6",2362:"21394205",2365:"0e25a72d",2395:"a6928bfa",2409:"fd964227",2481:"e6a40e1f",2489:"a499fd1a",2501:"be301c2c",2535:"714d131a",2567:"979e1acc",2594:"007a5dcf",2623:"bb7d6606",2625:"4820af3c",2640:"218089fc",2667:"14471590",2782:"586ba27f",2944:"67ff8205",2975:"36938d4d",2991:"5508e2ac",3054:"d06e53ba",3089:"c7e53bef",3109:"2d8e5a36",3122:"9b1f93b0",3149:"9fc1308e",3153:"5052ddc8",3160:"88ab90d0",3298:"c11e8b5e",3347:"cb6be3c1",3368:"36b3fd88",3369:"b1464a13",3393:"eece0504",3469:"8b6f670f",3471:"2b1600c1",3498:"2931718b",3608:"8a9335de",3671:"0ab3deb1",3708:"5fe8eea0",3880:"054dea55",4013:"ec00a2b1",4033:"584766a3",4050:"7a01ccde",4054:"5c6fd720",4063:"59ffa650",4071:"0503952e",4072:"75a93e06",4080:"ec5123a0",4083:"57c2517e",4192:"9fbbe61d",4195:"53da519e",4278:"cc3bb9f2",4454:"e7c516ee",4509:"4b6f4e1f",4539:"6bf079d7",4581:"e993335d",4656:"560aeec4",4662:"f6a2d74f",4690:"120c32d6",4738:"0c9c869d",4799:"a250a35c",4937:"2ea69f52",4972:"d3a97153",4993:"698b5d1a",5088:"25553683",5123:"5373e5ab",5137:"a77a9104",5170:"b087f47e",5193:"bc0b5208",5215:"9f468080",5221:"770def6a",5225:"f1220b02",5276:"b588e433",5338:"dc4f8022",5372:"210abec6",5465:"0711bfa1",5466:"e7af8c5b",5502:"640c35a7",5599:"95aa58b6",5605:"e70a84e8",5644:"dfd6d9f4",5658:"c0238936",5691:"e840285e",5701:"71837a23",5738:"d8598f6f",5864:"96be6b6f",5905:"e78c6961",5963:"3605b419",5979:"e84e6c54",5980:"561f8aa2",6048:"91f23ec5",6103:"6e629d0d",6175:"dfac47c1",6207:"ee2ab886",6316:"1e6f0db3",6346:"c2b1aa77",6497:"f6925244",6502:"dcca1205",6561:"3446a23f",6573:"846f5dd9",6586:"22244bbb",6611:"42b5547e",6792:"97cd7743",6811:"0a820794",6835:"84fe7cd7",6845:"179b6e00",6880:"f59d9bd2",6881:"d73ab82e",6979:"b81cdf4f",7024:"32b5ce12",7030:"591c92fd",7177:"3d222cfd",7232:"c5edba44",7313:"97421c3c",7370:"6c8ebb26",7419:"70783722",7431:"d4891614",7451:"79eb635d",7510:"ef21911a",7523:"a50fa31e",7595:"0566c907",7713:"6f8adb30",7724:"a682bf83",7756:"6ef8853e",7802:"8bad7040",7819:"18b19b89",7832:"d06ae912",7859:"2c63b326",7918:"da7860c7",8110:"67b445f8",8163:"9bb83661",8187:"15408acc",8188:"744b8f14",8224:"35cc8a82",8341:"57a51f36",8358:"4567adf4",8431:"664ba346",8446:"3afdb8fc",8520:"9d823df7",8527:"7a32ddc4",8610:"b052f9ac",8675:"bfccbf6c",8718:"1f3949ec",8764:"5b79616f",8779:"6f17d3c6",8843:"b6c101ac",8845:"6b6021cc",8853:"1722ffbc",8954:"a967f9d5",8994:"b9209919",9003:"8d55be7e",9049:"a6b0d0b3",9088:"1e6f46ff",9111:"0c2ba893",9125:"572fe957",9168:"ecb0feda",9200:"2a677a1e",9254:"894409da",9356:"057dfa1b",9479:"15ca8855",9481:"252302bf",9487:"868c3b2a",9513:"d4633977",9514:"5b96b506",9529:"a7291737",9594:"bf6000b1",9616:"bdf60722",9671:"ae157a10",9674:"dfe8f48d",9695:"624d19e4",9713:"2821960d",9844:"2e6e9ee4",9879:"e7e646d3",9885:"c3b27a40"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),d={},f="w3f-education:",r.l=(e,a,b,c)=>{if(d[e])d[e].push(a);else{var t,o;if(void 0!==b)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var f=d[e];if(delete d[e],t.parentNode&&t.parentNode.removeChild(t),f&&f.forEach((e=>e(b))),a)return a(b)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={12836233:"2064",14263310:"3369",15037438:"4054",17896441:"7918",56775098:"3109",61608705:"9088",79092693:"644",85386164:"6561",86710306:"6979",98650093:"5738",e159a34b:"30",b6e1accc:"45","935f2afb":"53",bf99f0d1:"79",dba49971:"162",a70cf35a:"166",bb3b9c27:"240","697abe16":"292","2a3afb9a":"437",b2b675dd:"533",f732ab87:"572",ac0fbae6:"682",c23220d2:"724",e36ee660:"898","47521d56":"901",a7071214:"927","0ec98211":"961","66f9d3f7":"1004","8a4ddfdc":"1121","51a84af5":"1158","6e12f270":"1209","8eae764b":"1244","0ee51ef9":"1309","0b931a33":"1413",b2f554cd:"1477","845bb328":"1516","1fd9a240":"1554","5cbab3ba":"1578",b5a07b06:"1621","20b9e0fb":"1652",a7023ddc:"1713",d630c86a:"1760",fa73f8cd:"1801","9af3c048":"1811",bf8854c0:"1818","38aea4d2":"1884","4d534538":"1954",e142862f:"1969","1b86bc4d":"2054","17abfe6e":"2156","1ecd3227":"2157","912dd27b":"2275","7e18d183":"2323","60080a18":"2362",be4f7283:"2365",cc3e0997:"2395","8d13930c":"2409","778bb0ff":"2481","18b60c58":"2489","9878ab02":"2501","814f3328":"2535","125052bb":"2567","89daa71f":"2594",e23acd1c:"2623","7ab8a3b9":"2625",b818a5fc:"2640","3f74b182":"2667",b0a340c1:"2782",d9ff3f6f:"2944","6baa9e40":"2975",bf0acaf4:"2991","04ff3f93":"3054",a6aa9e1f:"3089","3a78b9a2":"3122","860ac776":"3149",cdf6bb5b:"3153","8033be4e":"3160","142a84ed":"3298","698a20df":"3347",bed02607:"3368","9eca237c":"3393","0360b8e0":"3469","30d72593":"3471","85fcf0f9":"3498","9e4087bc":"3608","64e95fdc":"3671","55a851e8":"3708","4e20c90e":"3880","01a85c17":"4013",e3b93dc1:"4033",ec1fcf37:"4050","47047f6c":"4063","98813d19":"4071",a94eb289:"4072",dd33d834:"4080","1c093ed7":"4083","90dc3e28":"4192",c4f5d8e4:"4195","748750de":"4278",e66cda10:"4454","5f14d8ef":"4509","996ac392":"4539","89914d9a":"4581","4eee41d2":"4656",cfb4c858:"4662",ebc08f42:"4690","6f272f6d":"4738",e3852037:"4799","8a7c7b85":"4937",c41cffcf:"4993","7f498ece":"5088",c0a37a65:"5123","3878690d":"5137",c20d2866:"5170","75a26373":"5193","819e4c7d":"5215","36c7c7bf":"5221",d08ea62b:"5225","92a5cba8":"5276",d698cec0:"5338",df1189de:"5372","91599b81":"5465",f3700f36:"5466",ed7206fb:"5502","093f602f":"5599",bddd9d63:"5605",b1304a34:"5644",d6125dd8:"5658","10db19bd":"5691","4eeb835a":"5701","57376d8e":"5864",bdab9abd:"5905","611d03fc":"5963",dc107bf9:"5979","10b9b081":"5980",ccc49370:"6103","4a418313":"6175",a9f2f82e:"6207",fa1554a2:"6346","14bc938c":"6497",eedbd05c:"6502",f58a59cf:"6573","3cf1419d":"6586","650207e7":"6611","978caf70":"6792","5f5fedc0":"6811",ebaef5a3:"6835","58d818ae":"6845","22fea86a":"6880","939ada28":"6881","91bff8d5":"7024",acdb5c5d:"7030","5082a2db":"7177","6e1acb83":"7232","8d4aaee0":"7313",acb31fa2:"7370",a3b1e8a8:"7419",e74f0dea:"7431","5443c55a":"7451",d3564685:"7510","774d3ebf":"7523","9b6998de":"7595",b42777c1:"7713","8980ecfe":"7756","62695a46":"7802",abaf003d:"7819",e4beb833:"7832","3928d779":"7859","80bfa7c8":"8110","67f40c53":"8163","821660a9":"8187",d755f20e:"8188",b2def28e:"8224","4b1e850b":"8341",d44414ec:"8358","6743025c":"8431",c0f66a5e:"8446","4ea28616":"8520","1a7a00e4":"8527","6875c492":"8610",e25d2cc3:"8675","21c206d8":"8718","881c1402":"8764","59b286ba":"8779","4e7b4d15":"8843",ddd8c84d:"8845",f4d28708:"8853","5cad4cb9":"8994",f5be66f4:"9003",bb04187e:"9049",fb80b757:"9111",fe76b6c3:"9125","4a8e9470":"9168","0939dfa6":"9200","69e7bd09":"9254","37e718a0":"9356","08751a86":"9479","203cb511":"9481","4038b045":"9513","1be78505":"9514","8a655e4b":"9529","8568807f":"9594",e23e66c8:"9616","0e384e19":"9671",db9431d8:"9674","0aa99fa2":"9695",d651ce3a:"9713",f78c3b24:"9844","10533d3e":"9879","1233298f":"9885"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(a,b)=>{var d=r.o(e,a)?e[a]:void 0;if(0!==d)if(d)b.push(d[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var f=new Promise(((b,f)=>d=e[a]=[b,f]));b.push(d[2]=f);var c=r.p+r.u(a),t=new Error;r.l(c,(b=>{if(r.o(e,a)&&(0!==(d=e[a])&&(e[a]=void 0),d)){var f=b&&("load"===b.type?"missing":b.type),c=b&&b.target&&b.target.src;t.message="Loading chunk "+a+" failed.\n("+f+": "+c+")",t.name="ChunkLoadError",t.type=f,t.request=c,d[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,b)=>{var d,f,c=b[0],t=b[1],o=b[2],n=0;if(c.some((a=>0!==e[a]))){for(d in t)r.o(t,d)&&(r.m[d]=t[d]);if(o)var i=o(r)}for(a&&a(b);n Blog | Polkadot Education Initiative - +

· One min read
Radha

The technical education team at the Web3 Foundation is pioneering the initiative of creating an introductory course on Polkadot. This will be a Massively Online Open Course (MOOC) hosted on a platform that can issue credentials for course completion. The MOOC development will be in the open and will be driven by the contributions from the community which will be curated by Web3 Foundation.

- + \ No newline at end of file diff --git a/blog/Polkadot MOOC.html b/blog/Polkadot MOOC.html index 24f5beae1..dce4a04fa 100644 --- a/blog/Polkadot MOOC.html +++ b/blog/Polkadot MOOC.html @@ -5,14 +5,14 @@ Polkadot MOOC | Polkadot Education Initiative - +

Polkadot MOOC

· One min read
Radha

The technical education team at the Web3 Foundation is pioneering the initiative of creating an introductory course on Polkadot. This will be a Massively Online Open Course (MOOC) hosted on a platform that can issue credentials for course completion. The MOOC development will be in the open and will be driven by the contributions from the community which will be curated by Web3 Foundation.

- + \ No newline at end of file diff --git a/blog/archive.html b/blog/archive.html index 377dd68c1..6cab2d1c6 100644 --- a/blog/archive.html +++ b/blog/archive.html @@ -5,13 +5,13 @@ Archive | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/blog/tags.html b/blog/tags.html index eccef2e21..0dbcc8ec0 100644 --- a/blog/tags.html +++ b/blog/tags.html @@ -5,13 +5,13 @@ Tags | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/blog/tags/mooc.html b/blog/tags/mooc.html index ed2dbf081..c9828f73b 100644 --- a/blog/tags/mooc.html +++ b/blog/tags/mooc.html @@ -5,14 +5,14 @@ One post tagged with "MOOC" | Polkadot Education Initiative - +

One post tagged with "MOOC"

View All Tags

· One min read
Radha

The technical education team at the Web3 Foundation is pioneering the initiative of creating an introductory course on Polkadot. This will be a Massively Online Open Course (MOOC) hosted on a platform that can issue credentials for course completion. The MOOC development will be in the open and will be driven by the contributions from the community which will be curated by Web3 Foundation.

- + \ No newline at end of file diff --git a/blog/tags/polkadot.html b/blog/tags/polkadot.html index 95c63bacb..049d1a6f4 100644 --- a/blog/tags/polkadot.html +++ b/blog/tags/polkadot.html @@ -5,14 +5,14 @@ One post tagged with "Polkadot" | Polkadot Education Initiative - +

One post tagged with "Polkadot"

View All Tags

· One min read
Radha

The technical education team at the Web3 Foundation is pioneering the initiative of creating an introductory course on Polkadot. This will be a Massively Online Open Course (MOOC) hosted on a platform that can issue credentials for course completion. The MOOC development will be in the open and will be driven by the contributions from the community which will be curated by Web3 Foundation.

- + \ No newline at end of file diff --git a/docs/AtoZ/account.html b/docs/AtoZ/account.html index 8344323f4..6b7a94d94 100644 --- a/docs/AtoZ/account.html +++ b/docs/AtoZ/account.html @@ -5,13 +5,13 @@ A for Account | Polkadot Education Initiative - +

A for Account

A for Account

What is an account?

Accounts are also sometimes referred to as wallets or addresses. On blockchains, we need a place to hold our assets, and this is what accounts are used for. A user can have many accounts. On Polkadot and Kusama, unlike most other public blockchains, we can even give accounts on-chain, authenticated, and human-readable identities if we would like to.

What a Polkadot account public key looks like

14dQ7nC8YabzseZ5iqvG4EQnrkTCsUCwMbCP6nQWeKgjF6Ye

Accounts on Polkadot and Kusama are SS58-encoded key-network pairs. By representing an address as a combination of keys and network IDs, it makes sending assets to an address that is unusable much less likely (Ethereum only uses keys; imagine sending ETH from Ethereum mainnet to an account that only exists on Binance Smart Chain, which is another EVM-based chain and being confused as to why you can’t see the ETH on Binance Smart Chain). SS58 is a slight modification of the Bitcoin Base-58-Check encoding. By using different address prefixes, we can easily tell if an account is on the same chain as another. For example, Polkadot addresses start with a 1, Kusama is always with a capital letter, generic substrate addresses will start with a 5.

Creating an account

Like any blockchain address, Polkadot and Kusama accounts are controlled with public-private key pairs. This is a cryptographic way of creating a public-facing id that is intrinsically linked to a private id, which should be kept secret. The address generation starts with a mnemonic phrase which is then converted into a public-private key pair. Because different wallets use different ways to derive the public and private keys from the mnemonic phrase, there can be issues with deriving the same public and private key pairs from the same mnemonic on different wallets.

What a mnemonic phrase looks like

way pumpkin card castle crystal domain mystery chalk dog twin chronic image

Portability

Accounts can also be utilized across Substrate chains. Since addresses are the result of encoding a public key and a network ID, we can create an address for another chain by decoding the address, changing the network ID, and encoding the key with the new network ID, resulting in a new address that is controlled by the same underlying key. This allows you to use one mnemonic phrase across multiple Substrate chains.

- + \ No newline at end of file diff --git a/docs/AtoZ/bridge.html b/docs/AtoZ/bridge.html index c0a6bb2b5..1cdb77c59 100644 --- a/docs/AtoZ/bridge.html +++ b/docs/AtoZ/bridge.html @@ -5,7 +5,7 @@ B for Bridge | Polkadot Education Initiative - + @@ -14,7 +14,7 @@ Meanwhile, the original BTC is locked up in the BTC bridge, which will be accessible only by the original sender of the transaction. So if Bob ever wants to transfer the PBTC back into his BTC account, the same smart contract can burn the PBTC on the Ethereum blockchain, take the locked up 1:1 and send it back to Bob’s BTC account. This is all secured by the bridge's Collators, synchronized with the consensus of the bridged blockchain and the Polkadot blockchain.

See this video for a visual explanation of this process: How Bitcoin and Ethereum can Cooperate and Collaborate Through Polkadot with Bruno Skvorc

info

Learn more about how Bitcoin and Ethereum can cooperate and collaborate through Polkadot in this video by Bruno Skvorc.

How can we achieve Bridges?

There are a couple ways to develop a Bridge on Polkadot. Primary way is to use the Bridge pallet through the Substrate framework, which the Polkadot and Kusama blockchains are also built with. Another way is to use smart contracts, especially if the blockchain being bridged is not a Substrate chain. For example, bridging Ethereum will require this approach where a smart contract living on Ethereum can execute the logic that the bridge requires. And lastly we can use Higher-order protocols like XClaim, a base protocol on which we can build bridges on top of. This is only recommended if the asset being bridged does not have a smart contract platform on-chain, such as BTC.

What are some projects that’re building Bridges, and how can you build a Bridge?

Bridges are software that needs to be written and maintained, so what’re some examples of this in the real world? Interoperability in a decentralized, trustless way is a difficult endeavor. At Web3 Foundation, we fund different applications looking to build bridges, such as ChainSafe and Centrifuge, through our grants program. Another Web3 funded project called PolkaBTC has developed a Substrate bridge to BTC. And ChainX, as mentioned earlier, is a protocol that can be utilized to develop bridges, and a BTC bridge has been built using that. If you’re interested in building your bridge, start by looking into the Parity Bridges Common repository, where the team at Parity have put together a collection of valuable components when building bridges.

- + \ No newline at end of file diff --git a/docs/AtoZ/collator.html b/docs/AtoZ/collator.html index 364f957fc..a40cb7811 100644 --- a/docs/AtoZ/collator.html +++ b/docs/AtoZ/collator.html @@ -5,7 +5,7 @@ C for Collator | Polkadot Education Initiative - + @@ -14,7 +14,7 @@ Using Cross-chain message passing(XCMP), which will be covered in letter X, Collators can communicate with other Collators of different parachains. This is imperative for allowing multi chain applications to work seamlessly. If you’re curious about how the technical details of Collators work, check out Cumulous, a set of tools created by Parity to build Polkadot parachains.

Workshop/tutorial: https://substrate.dev/cumulus-workshop/#/
Cumulus code: https://github.com/paritytech/cumulus/.

- + \ No newline at end of file diff --git a/docs/AtoZ/democracy.html b/docs/AtoZ/democracy.html index 9c815768f..9737dc231 100644 --- a/docs/AtoZ/democracy.html +++ b/docs/AtoZ/democracy.html @@ -5,14 +5,14 @@ D for Democracy | Polkadot Education Initiative - +

D for Democracy

D for Democracy

Democracy is a form of governance that society has been practicing in different forms for centuries. At its purest form, it is the decentralization of power. It first appeared in the 5th century BC derived from the Greek word meaning “rule of the people'' and 26 centuries later, taking form in Polkadot, the application of Democracy can be thought of as “rule of the stakeholders”. It acts as the governance mechanism through which only agreed upon changes to the network by its stakeholders(participants) can pass. The network must function as a decentralized system, which also happens to be how it handles code updates. When a new functionality is proposed, the network participants, the DOT holders, vote on it. And if it passes the Democratic process, it will automatically call the set_code function, which will update the runtime code(a WebAssembly blob, which we will cover later this year with letter W) of the network. This is how Polkadot works around hard forks which most other blockchains need to use to update their code.

So how does the system work? The flow follows - proposal of referenda > voting > enactment. Please refer to the wiki for a deeper dive into each part.

Proposal of referenda

In Polkadot, the main application of Democracy comes from a public referendum. Referendums are simple, inclusive, stake weighted voting mechanisms, and they start with a proposal, which can be enacted in a few different ways:

  • Publicly submitted proposals
  • Proposals submitted by the council, either through a majority or unanimously
  • Proposals submitted as part of the enactment of a prior referendum
  • Emergency proposals submitted by the Technical Committee and approved by the Council

Voting on referendum proposals

Proposing requires you to bond some tokens, and once the proposal has been made, the tokens are locked until the proposal has been “tabled”. Meaning that there was a voting period. Bonded tokens have a lock period, and in Polkadot one lock period is 28 days, and Kusama is eight days. Once a proposal has been tabled, voting can begin. In reality, getting voting turnouts to be 100% is extremely rare, so we have developed Adaptive Quorum Biasing. In simple terms, this tool allows the network to intelligently adjust the amount of aye or nay votes required to successfully pass or not pass a referendum based on the participant turnout.

Enactment

Every approved proposal will have a period before it is enacted, that is, when the set_code function is called. We call this the delayed enactment period, and it allows participants that do not agree with the new update to leave the network. Also, those who voted for the proposal will have their bonded DOT locked until the upgrade is executed.

Council

Any DOT holder can become elected to the council. This a delegated group of stakeholders that are elected through a rotating approval vote, meaning that the seats in coun cil are voted on one at a time. Like a few other network parts, the elections use the sequential Phragmén method. Polkadot currently has 13 council seats and is planning to eventually have 24, while Kusama has 19. Candidates for council bond their DOT to be voted for, and their DOT is returned when elected. Top runner-ups are persisted until the next election, meaning their bond stays locked till the next seat is up for election. Council terms are seven days on Polkadot and one day on Kusama. They control the treasury, propose referenda, cancel dangerous and non-controversial referenda, and elect the technical committee. The existence of the council allows the network to have a trusted group that represents the absent participants to make sure Democracy is functioning as intended.

Check out this crowdcast which dives deeper into the governance of Polkadot.

- + \ No newline at end of file diff --git a/docs/AtoZ/existential-deposit.html b/docs/AtoZ/existential-deposit.html index 81d89fd75..d6dfbad3c 100644 --- a/docs/AtoZ/existential-deposit.html +++ b/docs/AtoZ/existential-deposit.html @@ -5,13 +5,13 @@ E for Existential Deposit | Polkadot Education Initiative - +

E for Existential Deposit

E for Existential Deposit

Existential Deposit and reaping

Accounts must have a minimum balance to exist on-chain. This minimum balance, an existential deposit (ED), is used as a financial incentive to keep wallets alive. If a wallet's balance falls below the ED, it is considered a dust account and will be removed from the on-chain data. Once all references to the account have been removed, the account can be reaped (wholly emptied).

The ED on Polkadot is 1 DOT and Kusama is 0.0000333333 KSM.

Reaping does not mean the account is no longer usable, however. The keys still exist in the wallet and can be used in the future, and funds can still be transferred to the account as long as the total funds in the account will exceed the ED. However, the reaped funds are not recoverable if the account was previously reaped. To protect your account from going below the existential deposit amount, the wallet on PolkadotJS has a keep-alive option that will not let transactions take place, which would otherwise put the account balance under the ED amount.

If you are a validator, refrain from depositing any staking rewards to a reaped account, or those funds will be lost and unrecoverable.

- + \ No newline at end of file diff --git a/docs/AtoZ/forkless.html b/docs/AtoZ/forkless.html index 7a6bf993b..6e1d8432a 100644 --- a/docs/AtoZ/forkless.html +++ b/docs/AtoZ/forkless.html @@ -5,14 +5,14 @@ F for Forkless | Polkadot Education Initiative - +

F for Forkless

F for Forkless

Forking

A fork is when a codebase is copied and developed separately from the original version. With open-source software, this is legal, though forks can also happen in licensed software, both legally and illegally. In the realm of blockchain and cryptocurrency projects, forks frequently occur, meaning not only do new copies of the codebase get created and developed as separate projects, but sometimes even specific updates on the same codebase require forks. Traditional blockchains like Bitcoin and Ethereum require forks because of their architecture. Bitcoin has been forked thousands of times and has hundreds of forks that became legitimate blockchain projects; each copy has been developed differently from the original. For example, Bitcoin Cash was a fork of Bitcoin, and so was Bitcoin Gold. But also some not-so-apparent forks include Ethereum and Litecoin. And sometimes, there are levels, for example, DogeCoin was a fork of Lucky Coin, which was a Fork of Litecoin, which is a fork of Bitcoin.

Runtime

Runtime is the environment in which a piece of software gets executed. This includes the hardware requirements and software changes. An upgrade in runtime is indicated by a difference in the version, i.e., 1.0.0 to 2.0.0

Hard Fork

A hard fork can be a software upgrade to a network or a split in the network. It is when clients on the network need to upgrade or switch over to the new version, or it will be incompatible with the latest runtime.

Soft fork

A soft fork is a backward-compatible upgrade. The clients on the network will not need to upgrade to a newer version to keep working.

Forkless

Substrate-based chains can upload the runtime bytecode as a Wasm(WebAssembly, which we will discuss when we get to the letter W) blob. This is done through Polkadot on-chain governance system. The next block will include the runtime upgrade if a new runtime is successfully voted on and the network agrees to the upgrade. The participants on the web will then get the latest runtime and start building blocks with the latest version. Forkless upgrades

Further reading

- + \ No newline at end of file diff --git a/docs/AtoZ/grandpa.html b/docs/AtoZ/grandpa.html index aba8515bc..38fb5e572 100644 --- a/docs/AtoZ/grandpa.html +++ b/docs/AtoZ/grandpa.html @@ -5,13 +5,13 @@ G for GRANDPA | Polkadot Education Initiative - +

G for GRANDPA

G for GRANDPA

GRANDPA(GHOST-based Recursive ANcestor Deriving Prefix Agreement) is the finality module of the Polkadot and Kusama blockchains.

Finality

In the world of blockchains, finality is when a new block has been added to the blockchain with confidence that it won’t be reverted, and the network agrees on this new copy of the chain. It happens to be an essential part of the consensus mechanism. A consensus mechanism, such as Bitcoin's Nakamoto consensus, has a probabilistic finality. The more blocks that get added after that block, the higher the probability that the block will be included in the canonical (network-approved) chain. However, this comes with risks - one can never be 100% sure that a block is canonical, although the more blocks built on top of a given block, the less likely it is. Various kinds of double-spend attacks rely on this feature, advertising a block that contains a transaction but does not stay as a part of the canonical chain.

Let's think about this in a real-world scenario:

Let's say we’re buying a cup of coffee. The finality of that transaction is when the cash is handed to the cafe, and the employee puts it in the register. Then, the cash value is recorded in the cafe's books, stored in an envelope, and deposited at their bank; after a few days, it becomes available in their account for payroll, rent, and other costs. During each of those steps, the probability that cash value will be reverted, meaning lost or stolen, will become less and less likely.

GRANDPA

Polkadot and Kusama have a hybrid consensus model that splits the responsibilities of consensus into two different modules: GRANDPA, the finality module, and BABE, the block production module. BABE (Blind Assignment for Blockchain Extension) produces blocks, and GRANDPA decides which blocks are finalized - that is, what is the provable canonical chain. Note that BABE has its own chain selection rule, but it is probabilistic; however, if there is an issue with getting consensus from the required number of validators for GRANDPA-derived functionality, the network reverts to probabilistic finality as opposed to stalling.

Each round of GRANDPA votes can finalize many blocks on the canonical chain, significantly increasing the speed of finalization. GRANDPA considers this sufficient for finality as long as ⅔ of validators agree on the canonical chain. If there is a case of less than ⅔ consensus on a new copy of the chain, GRANDPA has mechanisms to punish validators that vote on more than one new version of the chain.

Splitting up consensus allows us to define network assumptions into the modules, making the consensus model in Polkadot a deterministic model. Which we consider being more secure than a probabilistic model. Especially since block production can continue and not stall even when not enough validators are online, in this case, we fall back to the BABE chain selection. GRANDPA is a piece of code that can be plugged into any blockchain; it's designed to work on any blockchain implementation as the finality module as long as it is provided with the necessary inputs.

Further reading

- + \ No newline at end of file diff --git a/docs/AtoZ/hash.html b/docs/AtoZ/hash.html index eb23f67cc..daaf9ab3b 100644 --- a/docs/AtoZ/hash.html +++ b/docs/AtoZ/hash.html @@ -5,13 +5,13 @@ H for Hash | Polkadot Education Initiative - +

H for Hash

H for Hash

Hashing, in its simplest explanation, is a way to transform data. Depending on the underlying hashing algorithm, the transformation of that data happens differently. For example, Bitcoin uses the SHA-256 hashing algorithm (Simple Hashing Algorithm 2, 256bits), which returns a 256-bit output, usually represented as an alphanumeric string. Even the slightest change in the input will significantly modify the hashing algorithm’s output. In cryptography, this is known as diffusion. For example, here, we will add whitespace to the end of the input.

“Wow, such hash” = 9DD40341177F8ADD02DA4DE95A572D8BCBF8E07D51726FB51162442B13BB53D6

“Wow, such hash ” = 8973F8EEA2536243DE4E67AEE2181E3D4C6470B1BA64E0C632E5C91EEDC47108

No matter the length of the input data, the hashing function will always return the same sized output. It is a deterministic function, so it always produces the same output for the same input. This output is helpful in many ways, such as quickly verifying and validating data sets of any size.

Hashes are a fundamental component of blockchain technology, and we use them in many parts. For example, we can hash transactions and then query them by their hash. We can hash a collection of transactions and create a transaction root hash; we can hash block data which include transactions and metadata about the transactions, then use that output hash as the block's unique ID. In newer generations of blockchains, we hash function calls and their outputs; we even hash new runtime updates.

To understand blockchain, it is important to understand cryptographic hash functions. Well-designed cryptographic hash functions will have the following properties:

Properties of a Cryptographic Hash Function

Deterministic: The same input should always have the same output.

Quick computation: We need to ensure that the calculation of the function is fast.

Preimage resistance(confusion): We need to be sure that the hashing output cannot be reverse-engineered to find out what the original input was.

A slight change in the input changes the output(diffusion): Even the slightest change in the input should alter the output significantly.

Collision-resistant: We need to be sure that each unique input maps to an individual output.

Second preimage resistance: Given a hash function and its output, we should not be able to generate an input (or inputs) that, when hashed, produce the same result.

Hashing vs. Encryption

Hashing and encryption are related but different. Hashing is a one-way function, meaning that we should not be able to reverse engineer the original input from just the output. Encryption, on the other hand, is a two-way function. Data(plaintext) can be hidden(encrypted) by transforming it to ciphertext with the encryption key. The ciphertext can then be transformed into the original plaintext by decrypting the ciphertext with the decryption key. The encryption and decryption key may be the same value or different, depending on the type of cryptosystem used.

Hashing on Polkadot

Polkadot and Kusama use an implementation called BLAKE2b, based on the BLAKE2 cryptographic hashing algorithm. BLAKE2 is also faster than SHA-2 and SHA-3, with similar or better security than SHA-3. We chose BLAKE2 for its security, speed, and simplicity of implementation. The BLAKE2b implementation is optimized for 64-bit platforms and can be twice as performant compared to running the SHA-256 algorithm on a similar machine. Due to these advantages, the project ZCash transitioned to using BLAKE2 hashing from SHA-256, projecting that BLAKE2 is improving over time while SHA-256 is getting worse.

Blake2b hashing would look like the following:

“Wow, such hash” = 
e530f3d3ddfc24213167e7c601733a1b688e6cf362277b46f1135e348aa63477

“Wow, such hash ” =
f299f5ee1731f755d0f58bdc54ef3b0cadb94f7f959c3b18627e12138ed5c70e
- + \ No newline at end of file diff --git a/docs/AtoZ/interoperability.html b/docs/AtoZ/interoperability.html index 43a0dbd13..feb031ab1 100644 --- a/docs/AtoZ/interoperability.html +++ b/docs/AtoZ/interoperability.html @@ -5,14 +5,14 @@ I for Interoperability | Polkadot Education Initiative - +

I for Interoperability

I for Interoperability

Interoperability is at the core of the Polkadot philosophy. We aspire to combat chain maximalism and believe that the ecosystem is healthier when blockchains can communicate with each other. That’s why we call ourselves a Layer 0 multi-chain platform, and with that comes the technical challenge of connecting isolated blockchain networks. In this post, I will explain what interoperability means, the difference between Layer 0 and Layer 1 chains, how we use cross-consensus messaging(XCM) to achieve cross-chain communication of arbitrary data, and what the different ways to connect to Polkadot.

A little history: Interoperability of isolated internet networks

Starting in the early 70s up to the 80s, the internet began as isolated networks across universities, government agencies, and the military. Research fueled by these participants and an excited community of developers from around the globe led to the development of Transmission Control Protocol and Internet Protocol(TCP/IP), which allowed these isolated networks to connect, locate and interoperate with each other. We can think of the current state of the blockchain ecosystem as the early days of the internet, a collection of isolated blockchains that still have yet to connect under a protocol that allows them to communicate with each other. Polkadot seeks to become that protocol that will allow blockchains to interoperate. That’s why we call it a Layer 0 blockchain/protocol.

Layer 0 and Layer 1

From a societal development perspective, we scaled as communities into nation-states partly due to the specialization of skills. Members of the society can choose to develop their skills and specialize in areas like medicine, economics, politics, and manufacturing, among other things; they are also able to tap into an economy of trade where those specialized skills have a marketplace. We can think of those specializations as Layer 1 solutions: in the blockchain space, these are Bitcoin and Ethereum. And we can also think of the economy and marketplace as Layer 0. This is what Polkadot is aiming to be in the blockchain ecosystem.

XCM

Cross-Consensus Messaging (XCM) is a messaging format/paradigm that allows interoperability of Layer 1 blockchains. It is implemented at Layer 0 and has multiple message-passing functionalities. From Layer 1 to Layer 1, there is Cross-chain Message Passing(XCMP). From Layer 1 to Layer 0, i.e. from parachains to the relay chain, there is Upwards Message Passing (UMP), while from Layer 0 to Layer 1 (i.e. from the relay chain to parachains) we have Downwards Message Passing(DMP). XCM is un-opinionated about the structure of the chains on the sending and receiving end and can pass information unstructured to many types of chains. Hence, allowing any chain to utilize it for interoperability. We will cover this in more detail when we get to letter X.

Interoperable chains: parathreads/parachains/bridges

Now that we understand the concept of Layer 0 and Layer 1, we can talk about the types of connections Layer 1 chains can have on Polkadot.

Parachain: Parachains are Layer 1 solutions that will benefit from shared consensus and the pooled security of Polkadot. Parachain slots are slowly released by the network where potential parachain projects can place bids as parachain candidates. Most projects choose to do a crowdloan as a fundraising mechanism to increase their bid, hence their probability of winning a slot. Once a slot is won, the parachain project gets onboarded to Polkadot and is now a part of the ecosystem.

Parathread: All Parachains are, by default, Parathreads. Parathreads are under-the-hood parachain slots but allow multiple chains to reside in them, allowing chains to participate in consensus on a per-block basis. It might not make sense economically for some projects to live in a parachain slot full time; for those projects, we have parathreads.

Bridge: Bridges are for established, economically sovereign chains with an already established network. There are still ways for those networks to connect to Polkadot and benefit from the economy.

- + \ No newline at end of file diff --git a/docs/AtoZ/kusama.html b/docs/AtoZ/kusama.html index 898ebebc2..6092e4480 100644 --- a/docs/AtoZ/kusama.html +++ b/docs/AtoZ/kusama.html @@ -5,13 +5,13 @@ K for Kusama | Polkadot Education Initiative - +

K for Kusama

K for Kusama

It is known as the canary network for Polkadot and has been called a “value-bearing test network” by Gavin Wood. At its core, Kusama is a blockchain network that shares an almost identical code base with Polkadot, with the caveat that it might be slightly more advanced since code gets tested first on Kusama and vetted before moving onto Polkadot (but not always); the slight differences in parameters make it a good testing ground, but also a great place for startups and teams that want to move fast. For example, this postmortem of when a slashing bug was found on Kusama and fixed before it could impact Polkadot. As the slogan goes, “Expect Chaos”. The Kusama community tends to be a bit more developer-focused and smaller than the one on Polkadot. And even though it started as a value-bearing test network, we are now seeing projects that will only deploy on Kusama with no plans to deploy to Polkadot, so we are witnessing Kusama become its own fully-fledged network in its own right.

Some functionality differences between Kusama and Polkadot

There are runtime differences that make Kusama more conducive to testing out features. In general, Kusama is 4x as fast as Polkadot. This allows for an environment where runtime changes and parachain features can be tested “quicker”. Not everything is 4x as fast; for instance, the block time and speed of the chain grow is the same, at 6 seconds per block. Some of the main differences:

ParameterPolkadotKusama
Existential Deposit1 DOT33.3333 microKSM (0.0000333333 KSM)
Epoch4 hours1 hour
Era24 hours6 hours
Democracy
Voting Period28 days7 days
Launch Period28 days7 days
Enactment Period28 days8 days
Staking
Term duration1 day6 hours
Nomination period1 day6 hours
Un-bonding duration28 days7 days
Parachains
Max lease period2 years1 year

The 4x faster environment allows for more experimentation, such as passing new runtime upgrades quicker; this requires diligence from the participants to stay up to date. Regarding parachain slots, the team at Parity has suggested that the Kusama to Polkadot ratio will be 4:3, meaning Polkadot should have a 75% parachain slot capacity as Kusama to ensure stability guarantees.

Testing in the world of blockchains

We need Kusama because Web 3.0 functionality has high stakes due to blockchains' monetary aspect. It is important to note that testing features of blockchain networks is more complex than features of Web 2.0 applications. Failed features of blockchain technologies can lead to significant financial losses. The Ethereum DAO story is a perfect example, where a smart contract was drained of $150 million worth of ETH. In Web 2.0 applications, testing a feature can be as easy as putting it on a test server where it can be confirmed to work before it is released into production. This effort can take anywhere between a couple of minutes to a day or two, depending on the size of the feature. Blockchains, however, have a few aspects that make testing more difficult. Blockchains are heavily game theoretic; participants’ behavior is incentivized through value-bearing tokens (cryptocurrencies). If for some reason, a runtime upgrade contains some gameable bug, this can be difficult to foresee and could take the malicious participant a while to find. Hence, to test changes to code, we need a network where the real value is being used, and we can find such errors there. Kusama acts as that value-bearing network. When functionality is tested, and the code is audited, we can be more confident of its validity before putting it on Polkadot.

Kusama becoming its own network

It is interesting to see that the canary paradigm of Kusama also reflects in the parachains deployed on it. Projects in the Polkadot ecosystem tend to test their functionalities on their respective Kusama parachain first and then plan to deploy them on their Polkadot parachain. However, we are slowly seeing projects that still need plans to deploy their projects to Polkadot and only stay on Kusama.** **Both chains are running smoothly and are onboarding more parachains as we speak. As of today (Jan 7, 2022), Kusama has 16+1 and Polkadot has 5+1, the +1 being Statemine and Statemint, respectively, the common-good parachains deployed and maintained by the team at Parity. To learn more about Kusama, check out the guide and check out all the parachains on Kusama here. Kusama has funded 72,590KSM to proposals through its Treasury, including a few chain state scanners like Subscan and Polkascan and wallets like Fearless and Polkawallet. Check out all the Treasury activity on Kusama here.

- + \ No newline at end of file diff --git a/docs/AtoZ/launch.html b/docs/AtoZ/launch.html index d93e14fea..248f64eb2 100644 --- a/docs/AtoZ/launch.html +++ b/docs/AtoZ/launch.html @@ -5,13 +5,13 @@ L for Polkadot Launch | Polkadot Education Initiative - +

L for Polkadot Launch

L for Polkadot Launch

At the end of 2021, Polkadot hit an important checkpoint with the rollout of parachains, marking the final phase of the original promise of the whitepaper that was published in 2016 and the Genesis block time stamped on May 26, 2020, at 15:36:21 UTC, marked the first phase of Polkadot’s launch. A planned, phased, and controlled process started to help the network transitioning from being centralized and closely controlled to being decentralized. More than five years have passed since the original whitepaper. In this post, I’d like to explain the phases the network went through and the reasoning behind them.

Phases (2016 - 2022)

Proof of Authority (PoA) [May 26, 2020]

The Polkadot network started as a Proof of Authority network, a consensus mechanism that gives a small number of validators complete control over the network. It was maintained by six validators that Web3 Foundation controlled. With the power of Sudo (a Substrate functionality that allows for a single account to do anything to the chain using root, though a bit technical, you can learn more about it here and here), the Foundation closely nourished the early stages of the network. Users were allowed to claim their DOT tokens and stake them. However, during this phase, users' staking only meant signaling their intention to be a validator or nominator. The Foundation used this as a metric to know when there were enough validators to remove some control over the network and put it into the hands of the DOT holders.

Nominated Proof of Stake (NPoS) [June 18, 2020]

Once there was enough signal from the intended validators, it was time to start the next phase. This would entail a transition of the network from PoA to NPoS. The Foundation did this by using a Sudo call to change the Eras, which triggered the first validator election. This phase marked the first efforts to decentralize the network further. Also, Sudo was used to increase the active validator set slowly during this phase.

Governance and Sudo removal [July 20, 2020]

Governance on Polkadot is how the DOT token holders enact runtime upgrades. The only way to remove Sudo from the Foundation’s control was through referendums in which the DOT token holders could participate. This would be done by the Foundation enabling Governance (using Sudo, of course), in which the network can elect Council and Technical Committee members and accept public proposals. With Governance in place, Sudo was removed through public referenda, and the root power was transferred to the DOT holders.

Balance Transfers [August 18, 2020]

With Sudo removed, anything from here on out required Governance to enable runtime upgrades. A public proposal allowed balance transfers on block number 1,205,128 by the DOT token holders. With this runtime upgrade, DOT holders could now transfer funds between accounts.

Parachain Rollout [October 13, 2021]

With over a year of development efforts, parachain functionality was first tested on the Rococo test network and then released via governance on Kusama before being announced during the Sub0 conference that parachain functionality was ready for Polkadot in October of 2021. With Motion 118, proposing two batches of auction parachains on Polkadot were on the way. The DOT community voted to pass this motion. Auctions were set to start on November 11, 2021, and the first five parachains (Acala, Moonbeam, Parallel Finance, Astar, and Clover) were onboarded on December 18, 2021. Currently(January 14, 2022), the 2nd batch of auctions are well on their way, with the 2nd batch due to get onboarded on or around March 11, 2022.

Future [2022 - infinity]

As the rollout suggests, an important goal is making Polkadot as decentralized as possible. One suggested next step in this direction is to remove the Council and the Technical Committee as they’re both points of centralization, and a new governance model is being developed that does exactly that. In addition to that endeavor, there are several proposals for the following stages of the network, and Robert Habermeier recently talked about some upcoming efforts. These include async backing, a way to build and bundle validated parachain blocks before writing them to the Relay Chain, parathreads, and especially, a way to schedule parachain slot availability on a block-by-block basis. Some network optimizations have also been proposed, focusing on improving collator and validator interactions.

- + \ No newline at end of file diff --git a/docs/AtoZ/multisig.html b/docs/AtoZ/multisig.html index 0e05b1b59..3336d6f85 100644 --- a/docs/AtoZ/multisig.html +++ b/docs/AtoZ/multisig.html @@ -5,13 +5,13 @@ M for Multisig | Polkadot Education Initiative - +

M for Multisig

M for Multisig

To have true ownership of your assets on the blockchain, we use accounts, and these accounts have addresses. An account is managed with keys: a public key with corresponding private key. Ownership of an account can be verified by the ability to prove you have the private key, which allows you to sign transactions. Without the private key, accounts on the blockchain are not usable. Losing your private key is equivalent to losing your wallet forever.

Sometimes we need multiple accounts to sign off on transactions; think shared bank accounts or the government passing a bill requiring a certain amount of signatories. This is where multi-signature accounts come in. A multisig can comprise two or more accounts, and a signature threshold needs to be defined. This value equals or is less than the number of accounts part of the multisig. The threshold is the number of signatures required to sign the transactions on the multisig to execute the transaction. Multisig accounts can execute any on-chain function that a regular account can, including staking, governance, and simple transfers. However, you should exercise caution when using a multisig for staking or crowdloans, which are time-sensitive.

Kind of cool

You can determine a multisig account address even if it hasn’t been generated. All you have to do is know the accounts part of the multisig and the threshold number. Using these inputs, we can determine the multisig address. An interesting byproduct is that you can send a transaction to a non-existent address. If the accounts you chose to be the participants of that address were to create the multisig sometime in the future, they could access the transaction.

For example, here, I will take the Kusama stash addresses of Binance and Kraken and set a threshold of 2. This will generate the multisig address of those two accounts, with a threshold of 2. If I choose to, I can send KSM to that multisig, and if Binance and Kraken teams want to access that KSM, they would have to work together to set up the multisig.

--------------------------------
Addresses: E7ncQKp4xayUoUdpraxBjT7NzLoayLJA4TuPcKKboBkJ5GH F4xQKRUagnSGjFqafyhajLs94e7Vvzvr8ebwYJceKpr8R7T
Threshold: 2
Multisig Address (SS58: 0): 15XPbLctpFzEwA9qZgvYbsfEwPaBN9KVe7MFQao6bEur8sfX
--------------------------------

Some use cases

  1. Two accounts can be used as two-factor authentication(2FA) for a larger stash account. And even 3FA, or if you choose 4FA, is possible; you can create as many
  2. You can gather some friends, create a DAO or a corporation, and use a multisig account that holds the funds for the entity. When there is a transaction, all or most participants must sign off on it.
  3. Multiple accounts can join forces to run for a seat on a Council.

Be careful!

A couple of things to be aware of; once a multisig is created, it cannot be altered! And, if someone were to lose access to their account, which is a part of the multisig, the multisig could be unusable, which would be very bad. In addition, a multi-sig, once created, needs to have an existential deposit to be usable. Substrate allows runtime developers to set a fixed amount of maximum accounts that can be a part of a multisig; in the case of Polkadot, this number is 100, and on other Substrate chains, this value might be different.

Learn more and create a multisig…

Check out this video by our Tech Ed team to learn how to create and use a multi-sig. Also, check out our Wiki page for a slightly more technical and deep dive into how multi-sigs work.

- + \ No newline at end of file diff --git a/docs/AtoZ/npos.html b/docs/AtoZ/npos.html index 364cd615d..e285b75ea 100644 --- a/docs/AtoZ/npos.html +++ b/docs/AtoZ/npos.html @@ -5,13 +5,13 @@ N for Nominated Proof of Stake (NPoS) | Polkadot Education Initiative - +

N for Nominated Proof of Stake (NPoS)

N for Nominated Proof of Stake (NPoS)

Nominated Proof of Stake (NPoS) is a flavor of Proof of Stake (PoS); a consensus mechanism that allows token holders to stake (lock) their tokens on the network for the right to participate as validators or nominators. To understand this better, first, we will talk about consensus in general. We will cover Proof of Work (PoW) and PoS, followed by an introduction to NPoS, and wrap it all up with how the security of a network is directly related to its consensus implementation.

Consensus (noun):

  1. A general agreement about something.
  2. An idea or opinion that everyone shares in a group.

In a blockchain network, participants (nodes) have a full or partial copy of the blockchain on their computers. By using consensus mechanisms, they can ensure the data is synchronized with other participants across the network. There are different ways to achieve consensus, but in almost every case, network participants are tasked with adding data to the blockchain. There need to be well-designed economic incentives for these participants to behave truthfully and maintain the network's data, creating conditions that make it expensive to misbehave. This does not mean misbehavior is eliminated. However, the probability of it occurring is significantly reduced.

Since the introduction of Bitcoin and Nakamoto consensus in 2009, there have been quite a few developments in achieving decentralized consensus. PoS is one of the few that have gained popularity, mainly because it solves some of the issues that come with PoW, such as energy consumption, economic entry barriers to become a validator and problems with centralization in validator pools.

PoS is not perfect either; that is why there are different variants of PoS to address one of the main problems we will cover in detail; large token holders potentially controlling the network by becoming the majority of the validators.

A brief description of Proof of Stake

As mentioned, token holders can stake (lock) their tokens to become validators. Like miners in PoW, the validators in PoS are tasked with ordering transactions and creating blocks. For doing this work truthfully, they are rewarded with newly minted tokens (staking rewards). Once locked, those tokens are subject to slashing, aka a token burn, which acts as an incentive mechanism so that validators do not misbehave. If they misbehave ( and there are different severities of bad behavior), their tokens are subject to a slash (partially or fully). Most PoS systems require a minimum staking amount, which becomes a barrier to entry. And to provide better decentralization and equitability, there need to be good validator election systems in place not to favor larger token holders.

Proof of Stake vs. Proof of Work

PoW is a consensus mechanism in which miners successfully solve computationally intensive problems to create blocks. Anyone with adequate hardware, access to the internet, and electricity can become a miner. However, as more miners join the network, the difficulty of mining increases. This causes the competition to be centralized toward those who can afford better hardware and pay lower electricity costs. Hardware frequently gets improved, constantly rendering even recent machines out of competition, and cheap electricity has a geopolitical implication that makes it difficult for those who live in certain parts of the world where electricity might be expensive to participate in the network as a miner. These two aspects have caused the centralization of mining efforts in PoW networks. PoS solves these issues.

  1. Energy efficiency: PoS is considerably more energy efficient. Recent study done by the Crypto Carbon Ratings Institute Ava Labs found Polkadot to be the most cost efficient PoS blockchain.
  2. Lower barrier to entry: PoS doesn’t have hardware or energy source barriers; token holders can become validators if they meet the minimum.
  3. More conducive to decentralization: simply, it is easier to become a validator in a PoS system; this, in theory, will create conditions for better decentralization.

Despite the benefits, the minimum amount to stake may only be feasible for some token holders. For instance, Ethereum 2.0 has a minimum stake amount of 32 ETH, and Polkadot has a current minimum amount of approximately 300 DOT to become a nominator; however, efforts to reduce this amount are being worked on.

Nominated Proof of Stake

NPoS is the validator election mechanism of the consensus protocol on Polkadot, which gives token holders two different options to participate.

  1. Validator, a participant who runs a full blockchain node and builds blocks to be added to the chain. The cost of running a node is something to account for when considering becoming a validator.
  2. Nominator, a participant who does not need to run a full blockchain node but is tasked with electing well-behaved validators to be a part of the active validator set.

Both roles are required to stake tokens. The total staked amount for a validator is the sum of their stake and the stake of the nominators backing them.

caution

Consensus on a blockchain is a multistep process, and NPoS is simply the part that decides who can be a validator on the network. It does not contain the logic for adding a block to the chain or chain finality. To have the full picture of consensus on Polkadot, read about BABE the block production mechanism and GRANDPA the finality mechanism, in addition, to fully grasp Polkadot’s hybrid consensus implementation.

There are elections in which nominators elect validators to an active validator set. A fixed number of validators (297 on Polkadot and 1000 on Kusama) are subject to change via a runtime upgrade that passes governance.

Validator nomination, active set, and staking rewards

A new active set of validators are elected each era. An era is 24 hours on Polkadot and 6 hours on Kusama. Nominators are tasked with nominating up to 16 validators every election. The network then chooses the highest staked validators into the active set, and for the whole era, these validators will be tasked with creating blocks. At the end of the era, staking reward amounts are based on the era points gained by validators. Era points are calculated based on payable actions performed by validators, such as producing blocks, creating validity statements, etc.

Phragmén method for elections and distribution of stake

The sequential Phragmén method is a system that is designed to elect multiple winners in an election. This is perfect for NPoS validator set selection, and in addition to that, the same method is used to evenly distribute staking rewards across the validators in the active set. This ensures validators are paid equally, regardless of their initial stake. There is a security benefit to this as well. The total stake of the active validator set is distributed evenly across the validators, so the cost of taking control of the active validator set is significantly increased by ensuring the least staked validator is not weaker than the highest staked validator. NPoS is optimized to guarantee proportional representation of the minorities, in addition to behaving as a reputation system, where nominators are incentivized to elect trusted validators.

Consensus mechanisms and security

In the end, consensus mechanisms are trying to solve the same problem, that is, to maintain data synchronicity across a decentralized network and maintain Byzantine Fault Tolerance (BFT). This directly impacts the network, meaning a network is only as secure as its consensus mechanism is fault tolerant. In PoW, the security of a network can be quantified by the amount of computation(work) that the miners are exerting. To successfully attack a PoW network, one has to exercise more than 51% of the computation to change the data on the chain and have the majority of the participants accept it. In PoS, the security of a network can be quantified by the amount of stake. To attack a network, one has to own more than 51% of the network's total stake to control the majority of the validators on the network and change data on the chain.

note

This attack is relatively easy on a PoW network in its early life stages. Still, on a PoS network, as long as the distribution of tokens from the beginning is decentralized, it will be much more challenging to execute. And as networks mature, PoS has a higher ceiling of potential decentralization as long as the tokenomics are sound.

At the current moment, there are a lot of exciting developments in PoS systems. This does not mean, though, that other consensus mechanisms do not exist, and indeed, there are many different approaches to consensus on blockchains. We are in the era of exploration, and seeing the competition of networks with other approaches is very exciting.

- + \ No newline at end of file diff --git a/docs/AtoZ/on-chain-governance.html b/docs/AtoZ/on-chain-governance.html index bd6db2e5d..91a0e6e54 100644 --- a/docs/AtoZ/on-chain-governance.html +++ b/docs/AtoZ/on-chain-governance.html @@ -5,13 +5,13 @@ O for On-chain Governance | Polkadot Education Initiative - +

O for On-chain Governance

O for On-chain Governance

To understand on-chain governance, we need to understand its origin and components, and how those two work together.

Council

The council is an on-chain origin whose purpose is to represent passive token holders. Though it is an influential authority group, candidates need to be elected into the council by token holders. So gaining favorability among the community is imperative to win a council seat. Council members have a few key responsibilities:

  • Initiating sensible referenda
  • Canceling uncontroversial, harmful, and malicious referenda
  • Managing the treasury
  • Electing members to the technical committee.

There are 13 council seats on Polkadot, with space for up to 20 runners-up (those waiting to become council members). And there are 19 seats on Kusama, with room for up to 19 runners-up. The council rotates (councilors get re-elected) every 7 days on Polkadot and every 24 hrs on Kusama.

Technical Committee

The Technical Committee is a group of teams that have proven their technical knowledge by successfully implementing a Polkadot runtime. Teams can be added or removed by simple majority votes in the Council.

The Technical Committee aims to safeguard against malicious referenda, implement bug fixes, reverse faulty runtime updates, or add new but battle-tested features. These changes through the technical committee are fast-tracked for voting and implementation.

Proposals

There can be two types of proposals on-chain. Public proposals, which are made by token holders. And Council proposals come in the form of external motions, either made by a Council member or a token holder. The council also has a unique internal motion, but these do not become referendums. Internal motions don’t require Democracy pallet functionality, as they handle matters that don’t require runtime changes, such as managing treasury proposals or electing technical committee members. Public proposals can be made by bonding the minimum token amount (currently 100 DOT on Polkadot). Once the proposal has been posted on-chain, token holders signal their support by endorsing it. Endorsing requires them to bond the same token amount as the account that initially posted the proposal. Amount bonded is the critical metric when choosing which proposals become referendums. A proposal with three accounts bonding 10 DOTs each outweighs 29 accounts bonding 1 DOT each. The bonded tokens will be released back to the original accounts when the proposal becomes a referendum.

Referenda

Council proposals will become referendums when a simple majority of council members agree on the proposal. This is how the council can initiate legislation. Once passed, it becomes a public referendum. And if token holders created the proposal, those with the highest amount of bonded tokens backing them become public referendums.

Each proposal type, public and council, have their on-chain queue. Proposals live in their respective queues, and every 28 days (on Polkadot), the most backed proposal will become referendums that can be voted on. The on-chain mechanism that chooses the following proposal to become referendums alternates between the public and council queue.

The timetable for referendums on Polkadot is every 28 days, which means that every 28 days, the most backed public proposal or the most recent council proposal becomes a referendum and will be up for a vote by the token holders. Unless there is an emergency referendum, there can only be one referendum for a vote at a time. By alternating queues every 28 days, assuming both queues are full of proposals, each respective queue will get their turn every 56 days. Active referenda can be viewed using chain explorers, such as Polkadot-JS UI or Polkassembly.

Voting

Token holders can choose from multiple levels of support for a referendum, anywhere from not bonding any tokens , which reduces the weight of their votes, or by bonding up passes for a range of periods to increase their voting power. This creates an incentive to dissuade vote selling and also a way to allow smaller token holders to increase their voting power by locking up tokens for more than the minimum required amount. This is called conviction voting. Based on the voting outcome, the referendum will either pass or fail. If it passes, it will be enacted, meaning the runtime upgrade will be included in an upcoming block.

Conviction Voting

Simply put, conviction voting, aka voluntary locking, is a mechanism that allows token holders to increase their voting power on referenda by locking up their tokens securely on-chain. Built-in multipliers have a minimum of 0.1x (meaning no bond) and a maximum of 6x (bonded for 896 days). Remember, a single lock-up period is equal to the timetable of a referendum, which is 28 days.

Lock up period (amount in eras)Multiplier
00.1
11
22
43
84
165
326

And the equation used to calculate your voting power is:

votes = tokens * conviction_multiplier

If you don’t lock up your tokens, your vote will be worth 1/10th of a regular vote. If you lock up for one voting period by bonding the same amount as the originator of the proposal, your vote will be worth one vote, and if you lock up for 32 periods, your vote will be worth 6x.

Conviction voting is there to help protect and keep a check and balance for smaller token holders by giving them the ability to increase their voting power.

Adaptive Quorum Biasing (Turnout Biasing)

We can think of this mechanism as a lever that changes the super-majority percentage needed for referenda to pass. Its importance lies in situations without clear majority backing or against a referendum. Voting turnout will cause this mechanism to fall into two categories, positive turnout bias and negative turnout bias, which control the simple majority variable.

All public proposals use what is called positive turnout bias, meaning that as the referendum turnout increases, the threshold of “aye” votes needed decreases. Positive turnout bias ensures that with lower turnout, only uncontroversial proposals can pass. For example, if the turnout is 25%, which is a low turnout, the super-majority needed is around 66%. As turnout increases, the required super-majority decreases. For example, if the turnout is 75%, the super-majority required will be 54%.

Council proposals that pass unanimously use negative turnout bias, meaning it is easier to pass with low turnout and requires the super-majority to deny it.

Canceling

There are a few cancelation methods that can be utilized at different points of a proposal's lifespan:

  1. If there is a unanimous agreement, the Technical Committee can cancel a proposal.
  2. If the proposal has become a referendum, and there was a last-minute issue, such as a bug in the runtime code. The Council, through a 2/3rds majority vote, can cancel the referendum.

A canceled proposal’s deposit will be burned, but in the past, there have been proposals to reverse the burned tokens if it was controversial.

The future (On-chain Governance 2.0)

Though imperfect, the model has worked well for the past two years. Ever since the first proposals, thoughts have been floating around about how some weaknesses could be improved in the future. The community has signaled concern about the low voter turnout and the council and technical committee being a central form of authority.

In efforts to address these issues and decentralize the Polkadot and Kusama networks even further, efforts to dissolve the council and technical committee have been a focus of the development team at Parity Technologies. The code that would make this possible has been merged into the Substrate code base, but has yet to be tested on Kusama, audited, and approved by the community via runtime upgrade. The goal of governance 2.0 is to be a more agile, inclusive, secure, and decentralized design.

If you would like to learn more about on-chain governance, check out some of these resources:

- + \ No newline at end of file diff --git "a/docs/AtoZ/phragm\303\251n.html" "b/docs/AtoZ/phragm\303\251n.html" index 7f0d34154..9c0bac320 100644 --- "a/docs/AtoZ/phragm\303\251n.html" +++ "b/docs/AtoZ/phragm\303\251n.html" @@ -5,13 +5,13 @@ P for Phragmén | Polkadot Education Initiative - +

P for Phragmén

P for Phragmén

Phragmén is a collection of election mechanisms emphasizing fair representation created by or inspired by Lars Edvard Phragméns work. In the late 19th century he noticed that the most popular political party occupied the Swedish parliamentary seats and that minority parties were not represented. In order to have better voter representation in parliament he designed a method which would distribute votes across seats fairly. This became known as the Phragmén method, which works well for elections with multiple winners. Since several methods are associated with Lars Phragmén, in this post, I will talk about the specific method used in Polkadot called sequential Phragmén (seq-Phragmén), and the newer optimization that will increase security qualities and improve the representation of voters called Phragmms.

How seq-Phragmén is used in Polkadot

Fair representation is important when electing a pool of validators in Nominated Proof of Stake (NPoS). Or when electing Council members. Both of these elections have many voters with varying stake, many candidates to elect or nominate, and many available seats in either the validator set or the council. seq-Phragmén has a property called proportional justified representation(PJR), which ensures that no candidate is over or under-represented; it is a method that finds a fair distribution of stake across the highest-backed candidates. By sequentially optimizing the elected set of candidates and the stake distributed across those elected, it gets us closer to ideals that would ensure high security of the network, better representation of token holders in elections, as well as decentralization of validators and council members. The algorithm has two critical roles:

  1. Elect the highest backed candidates: Make sure that only the candidates with the highest backing get elected to the active set of validators or council
  2. Distribute the stake evenly among them: Make sure all staked DOTs for a given election are as evenly distributed across the elected set as possible.

NPoS validator elections

For NPoS, the election needs to be designed to maximize security. NPoS nominators stake their DOTs to elect validators to build blocks. And since a proof-of-stake network's security partially depends on the amount staked in the system and how decentralized that stake is across the participants, seq-Phragmén needs to take into account three points and optimize those as much as possible with a given sensible computation input:

  1. Maximize the total amount at stake.
    • Meaning elect the most backed validators into the active set.
    • More staked DOTs = higher security.
  2. Maximize the stake behind the minimally staked validator.
    • Meaning distributing the total stake as evenly as possible among the elected validators.
    • This is an NP-hard problem, meaning it is computationally difficult and requires optimization.
  3. Minimize the variance of the stake in the active set.
    • Meaning the difference in stake between the most backed validator and the least backed validator is minimized.
    • Ensures higher security by raising the cost to attack the lowest-backed validator.

Aiming to optimize these properties of the NPoS validator set will increase the security of the network and the payout that validators and nominators will gain.

note

NP-hard problems tend to be computationally heavy and tend to require optimizations. When developing blockchain runtimes, it's essential to ensure computation on-chain is kept to a minimum or that it has no potential to stall block production.

Council elections

When it comes to electing Council members, seq-Phragmén is also used. In each election cycle, the election yields 20 top-backed potential accounts and then picks the top 13 backed to be in the active Council and 7 to be runner-ups.

note

Stake-backed voting might seem un-democratic at first sight, but it is straightforward to game a system that does not have a stake-backed voting system on a pseudonymous blockchain system. In a non-stake-backed system where one account has one vote, any entity could create multiple accounts, give their single vote to the same candidate, and make it look like many voters back them.

Off-chain Phragmén

Due to its computationally heavy process, seq-Phragmén is run preferably off-chain, and the result is submitted to the chain via a transaction. This is done by off-chain workers(OCW). And any validator node by default runs as an off-chain worker. This means that the computation happens on the validator's machine/hardware and is responsible for the person running the validator; the computation does not happen on-chain. A validator can turn off the OCW option if they choose to.

Phragmms

One of the risks of combating only underrepresentation is that some minorities may well end up being overrepresented, which is also troublesome for the goal of decentralization. Phragmms, the next stage of improvements, will enable an election resulting in the most verifiably fair representation of candidates based on given votes and stake. Though still computationally heavy, the beauty of Phragmms is its strong verification properties, which allow even an untrusted third party to run the elections privately and off-chain, and then prove to the network that the corresponding election results are fair and proportional. This opens up an excellent opportunity for the trustless computation of elections off-chain.

Resources

Solving the NPoS problem with Phragmén video by Kian Paimani

Polkadot Wiki: Sequential Phragmén

W3F Research: Overview of NPoS

Phragmms research paper

- + \ No newline at end of file diff --git a/docs/AtoZ/polkadot-js.html b/docs/AtoZ/polkadot-js.html index 974045a6e..46055a376 100644 --- a/docs/AtoZ/polkadot-js.html +++ b/docs/AtoZ/polkadot-js.html @@ -5,13 +5,13 @@ J for Polkadot JS | Polkadot Education Initiative - +

J for Polkadot JS

J for Polkadot JS

Polkadot.js is a collection of tools that interfaces with the Polkadot blockchain in very granular ways. Polkadot.js as a term has multiple moving parts that are worth mentioning:

  1. Polkadot.js UI: This is the hosted application that loads when you navigate to your browser and click apps wallet (hosted). This is also sometimes called the “Polkadot-JS UI”.
  2. Polkadot.js API: This is the JavaScript API, a reusable library to allow programs to interface with the functionality of Polkadot.
  3. Polkadot.js Extension: This Chrome extension allows you to manage your accounts and sign transactions. Note that all it does is sign messages; it has limited functionality compared to full-featured wallets and cannot connect to the Polkadot network.
  4. Polkadot.js codebase: The codebase contains all the code repositories required to have the suite of tools working. You can navigate to the codebase here.
  5. Polkadot.js Phishing List: The Phishing List website is a community-driven curation of a list of less-than-honest operators. This list of URLs and addresses is constantly updated, and the polkdot.js extension uses it as a source to warn you when you navigate to a URL included in the list and blocks the addresses from the apps UI. Users can also contribute suspicious sites and addresses if they come across them.

Polkadot-JS UI

This post will focus on the UI, a powerful web application with granular functionality support when interacting with the Polkadot blockchain. It is not just a wallet; it has more abilities than creating accounts or sending and receiving transactions.

Abilities

Among other things, it also allows us to:

  1. Participate in staking
  2. Take part in governance
  3. Contribute to parachain crowdloans
  4. Run Parachain auctions
  5. Query chain metadata
  6. Query on-chain data using RPC calls

Essentially, the UI allows you to perform all functionalities that a user can do on either the relay chain or any parachain (although the user interface may not be aligned precisely with the functionality of any individual parachain). If you’re building a Substrate based blockchain, you can utilize the Polkadot.js UI to test your code's functionality.

Interacting with the Polkadot JS UI involves either querying on-chain data or issuing an extrinsic. Let's talk about what that means exactly.

Querying On-chain Data

To populate the UI, the web application queries the Polkadot.js API. The API then queries a Polkadot node and uses JavaScript to return information that the UI will display on the screen. You can choose which node to connect to by changing it in the upper-left-hand corner of the screen.

Issuing an Extrinsic

Extrinsics are information from outside the chain and are included in a block. Extrinsics can be one of three types: inherents, signed and unsigned transactions. Most extrinsics made from the Polkadot JS UI will be signed transactions. Inherits are non-signed and non-gossiped pieces of information included in blocks by the block author, such as timestamps, which are “true” because a sufficient number of validators have agreed about validity. Unsigned transactions are information that does not require a signature but will require some spam prevention. Signed transactions are issued by the originator account of a transaction that contains a signature of that account, which will be subject to a fee to have it included on the chain.

Considerations

Concerns have been raised by the community about the complexity of Polkadot-JS UI . However, Polkadot.js UI aims to support as much functionality as the relay chain requires of its users. Every time there is a runtime update(which can be quite often), a potential change needs to be made on the Polkadot.js codebase. For example, with most 3rd party wallets, when there are runtime updates, they usually need to add support. Polkadot.js UI is focused less on a user-friendly UI but rather to support the Polkadot runtime without any bugs.

For more user-friendly but more straightforward wallet implementations, check out the wiki page where we list Parity-developed and Treasury-funded wallet projects

Please take a look at some of the educational content we have created to learn more about Polkadot.js

Introduction to Polkadot.js

Create an account using Polkadot.js

- + \ No newline at end of file diff --git a/docs/AtoZ/q-faq.html b/docs/AtoZ/q-faq.html index 83947195c..26a636222 100644 --- a/docs/AtoZ/q-faq.html +++ b/docs/AtoZ/q-faq.html @@ -5,13 +5,13 @@ Q for FAQ | Polkadot Education Initiative - +
-

Q for FAQ

J for Polkadot JS

For the past two-plus years that Polkadot has been live, questions from the community are frequently present in socials. In this post, I’ll be answering some of those questions. If you have any other questions that you think should be a part of this post, please leave a comment.

How does staking work?

Staking on Polkadot uses Nominated Proof-of-Stake(NPoS), a flavor of PoS that allows for two types of participants, nominators and validators. Validators are the entities that run a full version of the Polkadot blockchain as a node, and they secure the network by bonding a number of tokens, which in turn allows them to create blocks. Nominators are the entities that elect validators into the active validator set, which currently is 297 on Polkadot and 1000 on Kusama.

info

The active validator set is an arbitrary value that can be changed via governance.

The role of staking is a part of the consensus mechanism. The mechanism allows token holders to be the on-chain entities that secure the network by putting up their tokens as collateral. This incentive, baked into the protocol, allows any token holder to earn newly created tokens, which the network mints whenever there is a successful new block. In simple terms, this is the inflation portion of the monetary policy of the network.

The reason why its design is complex is due to avoid the pitfalls of staking models. Mainly that of a few entities controlling the majority of the stake. Proof-of-Stake systems all have a different flavor of choosing the staked entities as validators. And the goal here is not to favor certain entities more than others. That endevour in itself is a challenging problem to solve.

For a detailed dive into NPoS read the letter N post of this blog series. And also be sure to checkout the Polkadot wiki.

What is the Polkadot-JS UI?

Polkadot-JS is a developer-centric interface that allows for granular control of Substrate-based chains. The idea with Polkadot-JS is to be a place where all extrinsics of all pallets can be engaged. For a tool like Polkadot-JS to stay up to date with the ever-changing Substrate landscape, functionality is the primary goal, and the user interface has to be a secondary consideration. Therefore, we should consider Polkadot-JS as a featureful tool rather than a user-centric one. For more user-centric tools, try one of the many wallets that support Substrate.

Why does a parachain need to connect to the Relay Chain?

The Relay Chain provides security for parachains. On a more granular level, it also provides a marketplace for parachains to compete. Via the parachain auctions, this competition can be considered healthy, as it incentivizes good product development and disincentivizes scams. The security of the Relay Chain is inherited by the parachains, making them as secure as the Relay Chain. This is a big improvement to previous models of blockchain development - as previously blockchains would have needed to develop their network security from scratch.

How is Polkadot different from Cosmos?

A classic comparison. Polkadot vs. Cosmos has been one of the most debated topics as they are competitors for a chain model that allows other layer 1 chains to connect and interoperate. See this wiki page for more info about this topic.

Why do different networks have different addresses but the same pubkey?

Substrate-based chains use the SS58 format for generating their account keypairs. Different network formats are other representations of the same public key in a key pair generated by an address-generation tool. This results in addresses being compatible across networks as long as the format is converted correctly.

Why is the unbonding period 28 days and why can’t I earn staking rewards when I’m unbonding?

The unbonding feature is designed to ensure that those who stake are not simply able to remove their stake at will, which ensures security in the network. Technically you will earn staking rewards if you unbond in the middle of an era for that era; however, not for the following one. The time you will have to wait until your tokens serves as a cooldown. During this time, you cannot nominate and/or transfer, hence unable to earn staking rewards.

Is Kusama a testnet?

Kusama was intended to be a value-bearing test network, but since its inception, we have seen it become a sovereign network in its own right. Including a vibrant developer community and culture, as well as projects that live solely in Kusama, with no plans to move on to Polkadot. So why? Because, in the world of blockchains, we are dealing with real value. A bug in runtime code can be extremely costly, and to prevent this, runtime code needs to be tested in real value-bearing environments. In addition to Web 2.0 style testing, Web 3.0 needs to be run in the wild to see if any game-theoretic unpredictable behaviors emerge. Kusama is exactly that for Polkadot and any other parachain that plans to be on Polkadot. But now, it is also home to many projects that have found a home in the world of Kusama.

- +

Q for FAQ

J for Polkadot JS

For the past two-plus years that Polkadot has been live, questions from the community are frequently present in socials. In this post, I’ll be answering some of those questions. If you have any other questions that you think should be a part of this post, please leave a comment.

How does staking work?

Staking on Polkadot uses Nominated Proof-of-Stake(NPoS), a flavor of PoS that allows for two types of participants, nominators and validators. Validators are the entities that run a full version of the Polkadot blockchain as a node, and they secure the network by bonding a number of tokens, which in turn allows them to create blocks. Nominators are the entities that elect validators into the active validator set, which currently is 297 on Polkadot and 1000 on Kusama.

info

The active validator set is an arbitrary value that can be changed via governance.

The role of staking is a part of the consensus mechanism. The mechanism allows token holders to be the on-chain entities that secure the network by putting up their tokens as collateral. This incentive, baked into the protocol, allows any token holder to earn newly created tokens, which the network mints whenever there is a successful new block. In simple terms, this is the inflation portion of the monetary policy of the network.

The reason why its design is complex is due to avoid the pitfalls of staking models. Mainly that of a few entities controlling the majority of the stake. Proof-of-Stake systems all have a different flavor of choosing the staked entities as validators. And the goal here is not to favor certain entities more than others. That endeavour in itself is a challenging problem to solve.

For a detailed dive into NPoS read the letter N post of this blog series. And also be sure to checkout the Polkadot wiki.

What is the Polkadot-JS UI?

Polkadot-JS is a developer-centric interface that allows for granular control of Substrate-based chains. The idea with Polkadot-JS is to be a place where all extrinsics of all pallets can be engaged. For a tool like Polkadot-JS to stay up to date with the ever-changing Substrate landscape, functionality is the primary goal, and the user interface has to be a secondary consideration. Therefore, we should consider Polkadot-JS as a featureful tool rather than a user-centric one. For more user-centric tools, try one of the many wallets that support Substrate.

Why does a parachain need to connect to the Relay Chain?

The Relay Chain provides security for parachains. On a more granular level, it also provides a marketplace for parachains to compete. Via the parachain auctions, this competition can be considered healthy, as it incentivizes good product development and disincentivizes scams. The security of the Relay Chain is inherited by the parachains, making them as secure as the Relay Chain. This is a big improvement to previous models of blockchain development - as previously blockchains would have needed to develop their network security from scratch.

How is Polkadot different from Cosmos?

A classic comparison. Polkadot vs. Cosmos has been one of the most debated topics as they are competitors for a chain model that allows other layer 1 chains to connect and interoperate. See this wiki page for more info about this topic.

Why do different networks have different addresses but the same pubkey?

Substrate-based chains use the SS58 format for generating their account keypairs. Different network formats are other representations of the same public key in a key pair generated by an address-generation tool. This results in addresses being compatible across networks as long as the format is converted correctly.

Why is the unbonding period 28 days and why can’t I earn staking rewards when I’m unbonding?

The unbonding feature is designed to ensure that those who stake are not simply able to remove their stake at will, which ensures security in the network. Technically you will earn staking rewards if you unbond in the middle of an era for that era; however, not for the following one. The time you will have to wait until your tokens serves as a cooldown. During this time, you cannot nominate and/or transfer, hence unable to earn staking rewards.

Is Kusama a testnet?

Kusama was intended to be a value-bearing test network, but since its inception, we have seen it become a sovereign network in its own right. Including a vibrant developer community and culture, as well as projects that live solely in Kusama, with no plans to move on to Polkadot. So why? Because, in the world of blockchains, we are dealing with real value. A bug in runtime code can be extremely costly, and to prevent this, runtime code needs to be tested in real value-bearing environments. In addition to Web 2.0 style testing, Web 3.0 needs to be run in the wild to see if any game-theoretic unpredictable behaviors emerge. Kusama is exactly that for Polkadot and any other parachain that plans to be on Polkadot. But now, it is also home to many projects that have found a home in the world of Kusama.

+ \ No newline at end of file diff --git a/docs/Blockchain/Module1/bitcoin.html b/docs/Blockchain/Module1/bitcoin.html index 8ecda4d24..0ef9140c4 100644 --- a/docs/Blockchain/Module1/bitcoin.html +++ b/docs/Blockchain/Module1/bitcoin.html @@ -5,13 +5,13 @@ Introduction to Bitcoin | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module1/blockchain.html b/docs/Blockchain/Module1/blockchain.html index 58db3612f..0e17110fc 100644 --- a/docs/Blockchain/Module1/blockchain.html +++ b/docs/Blockchain/Module1/blockchain.html @@ -5,13 +5,13 @@ What is a Blockchain? | Polkadot Education Initiative - +

What is a Blockchain?

  • A data structure
  • Typically an ordered, back-linked list of blocks with data.
  • Each block is identified by a hash
  • The hashes protect the integrity of the data

Typically, the blocks in a blockchain contain blockheaders and a payload

Although the payload could be any arbitrary data, it is mainly used for

  • Record keeping - who, what, when, and why (ex: ledgers)
  • Self-executing code (ex: smart contracts)
  • Hashes of arbitrary data (ex: hashes of Images, videos as NFTs)

Introduction to Blockchain

A Few Definitions of Blockchain

"Blockchain is a shared, immutable ledger for recording transactions, tracking assets and building trust." - IBM

"Cryptocurrencies like Bitcoin and Ethereum are powered by a technology called the blockchain. At its most basic, a blockchain is a list of transactions that anyone can view and verify." - Coinbase

"Blockchain technology is an advanced database mechanism that allows transparent information sharing within a business network. A blockchain database stores data in blocks that are linked together in a chain. The data is chronologically consistent because you cannot delete or modify the chain without consensus from the network. As a result, you can use blockchain technology to create an unalterable or immutable ledger for tracking orders, payments, accounts, and other transactions. The system has built-in mechanisms that prevent unauthorized transaction entries and create consistency in the shared view of these transactions." - Amazon AWS

Is Blockchain a scam?

  • Blockchain and cryptography are the safeguards against scams and forgery
  • Get a few obvious misconceptions out of the way
  • Highlight the need for education and awareness

Are Blockchains Decentralized?

  • The power of redundancy
  • Distributed vs Decentralized
  • Decentralization is a spectrum
  • The power of decentralization

Centralization vs Decentralization

Types of Blockchains

  • Public blockchain networks
  • Private blockchain networks
  • Permissioned blockchain networks
  • Consortium blockchains

Blockchains in Action

  • Bitcoin
  • Ethereum
  • Polkadot

Run your own Blockchain!

For tech savvy learners

  • Walkthrough of Substrate Node Template
- + \ No newline at end of file diff --git a/docs/Blockchain/Module1/digital-money.html b/docs/Blockchain/Module1/digital-money.html index 300c8fa11..ae90b4c57 100644 --- a/docs/Blockchain/Module1/digital-money.html +++ b/docs/Blockchain/Module1/digital-money.html @@ -5,13 +5,13 @@ Dawn of Digital Money | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module1/ethereum.html b/docs/Blockchain/Module1/ethereum.html index e4b85f278..e925fe5b8 100644 --- a/docs/Blockchain/Module1/ethereum.html +++ b/docs/Blockchain/Module1/ethereum.html @@ -5,13 +5,13 @@ Introduction to Ethereum | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module1/money-properties.html b/docs/Blockchain/Module1/money-properties.html index 1122f1609..255aaa2ca 100644 --- a/docs/Blockchain/Module1/money-properties.html +++ b/docs/Blockchain/Module1/money-properties.html @@ -5,13 +5,13 @@ Properties of Money | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module1/money-trust.html b/docs/Blockchain/Module1/money-trust.html index 697eb8435..4c27f38a7 100644 --- a/docs/Blockchain/Module1/money-trust.html +++ b/docs/Blockchain/Module1/money-trust.html @@ -5,13 +5,13 @@ History of Money and Trust | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module1/web3.html b/docs/Blockchain/Module1/web3.html index 4f4a2ea4c..2c77a970a 100644 --- a/docs/Blockchain/Module1/web3.html +++ b/docs/Blockchain/Module1/web3.html @@ -5,13 +5,13 @@ Introduction to Web3 | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module2/cryptography.html b/docs/Blockchain/Module2/cryptography.html index 967b2a033..c1ddff1af 100644 --- a/docs/Blockchain/Module2/cryptography.html +++ b/docs/Blockchain/Module2/cryptography.html @@ -5,13 +5,13 @@ Cryptography and Blockchain | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module2/datastructures.html b/docs/Blockchain/Module2/datastructures.html index 3bb205672..62972613d 100644 --- a/docs/Blockchain/Module2/datastructures.html +++ b/docs/Blockchain/Module2/datastructures.html @@ -5,13 +5,13 @@ Blockchain Datastructures | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module2/decentralization.html b/docs/Blockchain/Module2/decentralization.html index fb9e222ec..902f34caa 100644 --- a/docs/Blockchain/Module2/decentralization.html +++ b/docs/Blockchain/Module2/decentralization.html @@ -5,13 +5,13 @@ Decentralization Trilemma | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module2/hash.html b/docs/Blockchain/Module2/hash.html index 4af5d7a0f..94c9a2310 100644 --- a/docs/Blockchain/Module2/hash.html +++ b/docs/Blockchain/Module2/hash.html @@ -5,13 +5,13 @@ Cryptographic Hashing | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module2/invincible.html b/docs/Blockchain/Module2/invincible.html index d8525318a..04e94bc6d 100644 --- a/docs/Blockchain/Module2/invincible.html +++ b/docs/Blockchain/Module2/invincible.html @@ -5,13 +5,13 @@ How Invincible is Bitcoin Network? | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module2/keypair.html b/docs/Blockchain/Module2/keypair.html index 92244582b..a857df8dc 100644 --- a/docs/Blockchain/Module2/keypair.html +++ b/docs/Blockchain/Module2/keypair.html @@ -5,13 +5,13 @@ Digital Signatures and Crypto-wallets | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module2/trust.html b/docs/Blockchain/Module2/trust.html index 2824eafb0..273d95cf7 100644 --- a/docs/Blockchain/Module2/trust.html +++ b/docs/Blockchain/Module2/trust.html @@ -5,13 +5,13 @@ Trust issues | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module3/PoS.html b/docs/Blockchain/Module3/PoS.html index 62aa94a77..be2491168 100644 --- a/docs/Blockchain/Module3/PoS.html +++ b/docs/Blockchain/Module3/PoS.html @@ -5,13 +5,13 @@ Proof of Staking | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module3/PoW.html b/docs/Blockchain/Module3/PoW.html index 39dd3c703..c03cbb447 100644 --- a/docs/Blockchain/Module3/PoW.html +++ b/docs/Blockchain/Module3/PoW.html @@ -5,13 +5,13 @@ Proof of Work | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module3/finality.html b/docs/Blockchain/Module3/finality.html index 11335fa7b..0b19538c9 100644 --- a/docs/Blockchain/Module3/finality.html +++ b/docs/Blockchain/Module3/finality.html @@ -5,13 +5,13 @@ Block finality | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module3/forking.html b/docs/Blockchain/Module3/forking.html index 90fc82629..455ea3dc4 100644 --- a/docs/Blockchain/Module3/forking.html +++ b/docs/Blockchain/Module3/forking.html @@ -5,13 +5,13 @@ Hardforks of Blockchain | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module3/hashpower.html b/docs/Blockchain/Module3/hashpower.html index c2ae184c4..cd7ec36ed 100644 --- a/docs/Blockchain/Module3/hashpower.html +++ b/docs/Blockchain/Module3/hashpower.html @@ -5,13 +5,13 @@ Bitcoin Hashpower | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module3/mining-hardware.html b/docs/Blockchain/Module3/mining-hardware.html index 5248100c4..b7d5135dd 100644 --- a/docs/Blockchain/Module3/mining-hardware.html +++ b/docs/Blockchain/Module3/mining-hardware.html @@ -5,13 +5,13 @@ Bitcoin mining hardware | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module3/mining.html b/docs/Blockchain/Module3/mining.html index 78d8638c5..922ccacbd 100644 --- a/docs/Blockchain/Module3/mining.html +++ b/docs/Blockchain/Module3/mining.html @@ -5,13 +5,13 @@ How Bitcoin blocks are mined? | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module4/consensus.html b/docs/Blockchain/Module4/consensus.html index c3a390334..7e9d75aa9 100644 --- a/docs/Blockchain/Module4/consensus.html +++ b/docs/Blockchain/Module4/consensus.html @@ -5,13 +5,13 @@ Network Consensus | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module4/distributed.html b/docs/Blockchain/Module4/distributed.html index c0b77718c..ef11b55aa 100644 --- a/docs/Blockchain/Module4/distributed.html +++ b/docs/Blockchain/Module4/distributed.html @@ -5,13 +5,13 @@ Introduction to Distributed Systems | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module4/gossip.html b/docs/Blockchain/Module4/gossip.html index 39c42055b..e6aee1dfc 100644 --- a/docs/Blockchain/Module4/gossip.html +++ b/docs/Blockchain/Module4/gossip.html @@ -5,13 +5,13 @@ How do Blockchain nodes communicate? | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module4/lightclients.html b/docs/Blockchain/Module4/lightclients.html index 9f5eec44b..8bb6a99cc 100644 --- a/docs/Blockchain/Module4/lightclients.html +++ b/docs/Blockchain/Module4/lightclients.html @@ -5,13 +5,13 @@ Light clients and Trustless Networks | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module4/network-challenges.html b/docs/Blockchain/Module4/network-challenges.html index ce4e05d19..dfab33bb8 100644 --- a/docs/Blockchain/Module4/network-challenges.html +++ b/docs/Blockchain/Module4/network-challenges.html @@ -5,13 +5,13 @@ Networking Challenges and Opportunities | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module4/networkstack.html b/docs/Blockchain/Module4/networkstack.html index fc9de56ff..0f4a46578 100644 --- a/docs/Blockchain/Module4/networkstack.html +++ b/docs/Blockchain/Module4/networkstack.html @@ -5,13 +5,13 @@ Network Stack | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module4/nodes.html b/docs/Blockchain/Module4/nodes.html index 761b624de..cfda666c7 100644 --- a/docs/Blockchain/Module4/nodes.html +++ b/docs/Blockchain/Module4/nodes.html @@ -5,13 +5,13 @@ Types of Network Nodes | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module5/enterprise.html b/docs/Blockchain/Module5/enterprise.html index d4f5bf49f..3a75569ed 100644 --- a/docs/Blockchain/Module5/enterprise.html +++ b/docs/Blockchain/Module5/enterprise.html @@ -5,13 +5,13 @@ Enterprise Blockchains | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module5/layer0.html b/docs/Blockchain/Module5/layer0.html index ead8a9022..f001e4f34 100644 --- a/docs/Blockchain/Module5/layer0.html +++ b/docs/Blockchain/Module5/layer0.html @@ -5,13 +5,13 @@ Layer 0 Blockchains | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module5/layer1.html b/docs/Blockchain/Module5/layer1.html index 324bdd955..9045afc49 100644 --- a/docs/Blockchain/Module5/layer1.html +++ b/docs/Blockchain/Module5/layer1.html @@ -5,13 +5,13 @@ Layer 1 Blockchains | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module5/layer2.html b/docs/Blockchain/Module5/layer2.html index 57ba2acd3..013b86ae5 100644 --- a/docs/Blockchain/Module5/layer2.html +++ b/docs/Blockchain/Module5/layer2.html @@ -5,13 +5,13 @@ Layer 2 Blockchains | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module5/layers.html b/docs/Blockchain/Module5/layers.html index bf830ade7..ed50d014b 100644 --- a/docs/Blockchain/Module5/layers.html +++ b/docs/Blockchain/Module5/layers.html @@ -5,13 +5,13 @@ What are Blockchain Layers? | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module5/zk-proofs.html b/docs/Blockchain/Module5/zk-proofs.html index ad35d5ba0..adc959db8 100644 --- a/docs/Blockchain/Module5/zk-proofs.html +++ b/docs/Blockchain/Module5/zk-proofs.html @@ -5,13 +5,13 @@ ZK Proofs and Applications | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module6/computing.html b/docs/Blockchain/Module6/computing.html index 84fd51cd0..f78bde624 100644 --- a/docs/Blockchain/Module6/computing.html +++ b/docs/Blockchain/Module6/computing.html @@ -5,13 +5,13 @@ Decentralized Computing | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module6/crypto-defi.html b/docs/Blockchain/Module6/crypto-defi.html index a95e42db4..5a0afe117 100644 --- a/docs/Blockchain/Module6/crypto-defi.html +++ b/docs/Blockchain/Module6/crypto-defi.html @@ -5,13 +5,13 @@ Cryptocurrencies and DeFi | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module6/dao.html b/docs/Blockchain/Module6/dao.html index c2468c8de..d77ee97d9 100644 --- a/docs/Blockchain/Module6/dao.html +++ b/docs/Blockchain/Module6/dao.html @@ -5,13 +5,13 @@ DAOs and Governance | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module6/future-web3.html b/docs/Blockchain/Module6/future-web3.html index 39d25bc78..0590abfbb 100644 --- a/docs/Blockchain/Module6/future-web3.html +++ b/docs/Blockchain/Module6/future-web3.html @@ -5,13 +5,13 @@ Future of Web3 | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module6/nft-meta.html b/docs/Blockchain/Module6/nft-meta.html index 1c68432ad..9315edc77 100644 --- a/docs/Blockchain/Module6/nft-meta.html +++ b/docs/Blockchain/Module6/nft-meta.html @@ -5,13 +5,13 @@ NFTs and Metaverse | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Blockchain/Module6/web3.html b/docs/Blockchain/Module6/web3.html index d1286a50e..1a5c21489 100644 --- a/docs/Blockchain/Module6/web3.html +++ b/docs/Blockchain/Module6/web3.html @@ -5,13 +5,13 @@ Introduction to Web3? | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section1.html b/docs/Parachain/beginner/section1.html index 6cc7b7f6c..08655e299 100644 --- a/docs/Parachain/beginner/section1.html +++ b/docs/Parachain/beginner/section1.html @@ -5,13 +5,13 @@ Parachain Architecture Overview | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section1/network-components.html b/docs/Parachain/beginner/section1/network-components.html index 5cc89c2ff..df15e7083 100644 --- a/docs/Parachain/beginner/section1/network-components.html +++ b/docs/Parachain/beginner/section1/network-components.html @@ -5,13 +5,13 @@ Polkadot Network Components | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section1/parachain.html b/docs/Parachain/beginner/section1/parachain.html index 7a820dacd..ee8081ea6 100644 --- a/docs/Parachain/beginner/section1/parachain.html +++ b/docs/Parachain/beginner/section1/parachain.html @@ -5,13 +5,13 @@ Parachain Architecture Overview | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section1/relay-chain.html b/docs/Parachain/beginner/section1/relay-chain.html index c849799fe..9f8ac1086 100644 --- a/docs/Parachain/beginner/section1/relay-chain.html +++ b/docs/Parachain/beginner/section1/relay-chain.html @@ -5,13 +5,13 @@ Relay Chain Architecture Overview | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section2.html b/docs/Parachain/beginner/section2.html index f0697b209..0d767cdd0 100644 --- a/docs/Parachain/beginner/section2.html +++ b/docs/Parachain/beginner/section2.html @@ -5,13 +5,13 @@ Dependency Installation | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section2/install-binary.html b/docs/Parachain/beginner/section2/install-binary.html index ae7d441f6..252c1f039 100644 --- a/docs/Parachain/beginner/section2/install-binary.html +++ b/docs/Parachain/beginner/section2/install-binary.html @@ -5,13 +5,13 @@ Install Local Binaries | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section2/install-template.html b/docs/Parachain/beginner/section2/install-template.html index 724151263..3ed4a187b 100644 --- a/docs/Parachain/beginner/section2/install-template.html +++ b/docs/Parachain/beginner/section2/install-template.html @@ -5,13 +5,13 @@ Install the Cumulus Parachain Template | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section2/running-chains.html b/docs/Parachain/beginner/section2/running-chains.html index 8ba846a3d..0e8923e50 100644 --- a/docs/Parachain/beginner/section2/running-chains.html +++ b/docs/Parachain/beginner/section2/running-chains.html @@ -5,13 +5,13 @@ Running the Relay and Parachain | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section3.html b/docs/Parachain/beginner/section3.html index 654b62bc6..7d846e441 100644 --- a/docs/Parachain/beginner/section3.html +++ b/docs/Parachain/beginner/section3.html @@ -5,13 +5,13 @@ Creating & Registering Parachain | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section3/creating-auction.html b/docs/Parachain/beginner/section3/creating-auction.html index 558eda654..38a82b0c0 100644 --- a/docs/Parachain/beginner/section3/creating-auction.html +++ b/docs/Parachain/beginner/section3/creating-auction.html @@ -5,13 +5,13 @@ Creating a fast-tracked auction | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section3/creating-parathread.html b/docs/Parachain/beginner/section3/creating-parathread.html index 6393d12ef..16896c4b6 100644 --- a/docs/Parachain/beginner/section3/creating-parathread.html +++ b/docs/Parachain/beginner/section3/creating-parathread.html @@ -5,13 +5,13 @@ Reserve your parathread | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section3/developing-parachain.html b/docs/Parachain/beginner/section3/developing-parachain.html index 81b5216a4..fea718aef 100644 --- a/docs/Parachain/beginner/section3/developing-parachain.html +++ b/docs/Parachain/beginner/section3/developing-parachain.html @@ -5,13 +5,13 @@ Developing your parachain with FRAME | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section4.html b/docs/Parachain/beginner/section4.html index 97ff4f437..8ca81f3f1 100644 --- a/docs/Parachain/beginner/section4.html +++ b/docs/Parachain/beginner/section4.html @@ -5,13 +5,13 @@ Moving Forward | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section4/initatives.html b/docs/Parachain/beginner/section4/initatives.html index f459c1ff6..f73f322f1 100644 --- a/docs/Parachain/beginner/section4/initatives.html +++ b/docs/Parachain/beginner/section4/initatives.html @@ -5,13 +5,13 @@ Ecosystem Initiatives | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Parachain/beginner/section4/road-to-production.html b/docs/Parachain/beginner/section4/road-to-production.html index 4756f9137..246917ffc 100644 --- a/docs/Parachain/beginner/section4/road-to-production.html +++ b/docs/Parachain/beginner/section4/road-to-production.html @@ -5,13 +5,13 @@ Road to Production | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module1/architecture.html b/docs/Polkadot/Module1/architecture.html index 6626284db..114da0d0c 100644 --- a/docs/Polkadot/Module1/architecture.html +++ b/docs/Polkadot/Module1/architecture.html @@ -5,7 +5,7 @@ Architecture of Polkadot | Polkadot Education Initiative - + @@ -16,7 +16,7 @@ trust-free interchain transactability between parachains. In other words, the issues that Polkadot aims to address are those discussed above: interoperability, scalablility, and weaker security due to splitting the security power.

Architecture - Polkadot vs Ethereum

Transaction fees - Polkadot vs Ethereum

- + \ No newline at end of file diff --git a/docs/Polkadot/Module1/becomeparachain.html b/docs/Polkadot/Module1/becomeparachain.html index c968bf1b9..022ee5042 100644 --- a/docs/Polkadot/Module1/becomeparachain.html +++ b/docs/Polkadot/Module1/becomeparachain.html @@ -5,7 +5,7 @@ Becoming a Parachain on Polkadot | Polkadot Education Initiative - + @@ -19,7 +19,7 @@ to the interface we specify. Some of these expectations are natural components of blockchains, hence the naming. However, other non-blockchain systems may also run as a Polkadot parachain as long as they satisfy the interface.

Parachain Slot Explained

Parachain Slot Availability

- + \ No newline at end of file diff --git a/docs/Polkadot/Module1/features.html b/docs/Polkadot/Module1/features.html index 9363e8913..46adbe46c 100644 --- a/docs/Polkadot/Module1/features.html +++ b/docs/Polkadot/Module1/features.html @@ -5,7 +5,7 @@ Features of Polkadot | Polkadot Education Initiative - + @@ -19,7 +19,7 @@ thousands of transactions per second.

Efforts are in place to upgrade Ethereum to Proof of Stake.

Sharding Explained

One prominent solution to scalability for blockchains is to run many chains in parallel, often called sharding. Polkadot is a multi-chain system that aims to gather the security power of all these chains together in a shared security system.

Polkadot - First Fully Sharded Blockchain

Polkadot - Future-proof

Polkadot - Interoperability

- + \ No newline at end of file diff --git a/docs/Polkadot/Module1/polkadot.html b/docs/Polkadot/Module1/polkadot.html index 87b34281e..2034040aa 100644 --- a/docs/Polkadot/Module1/polkadot.html +++ b/docs/Polkadot/Module1/polkadot.html @@ -5,7 +5,7 @@ What is Polkadot? | Polkadot Education Initiative - + @@ -17,7 +17,7 @@ Moreover, with the increase in number of projects the security that each one is providing individually becomes weaker. Polkadot aims to provide a scalable and interoperable framework for multiple chains with pooled security.

Introduction to Polkadot Network

Polkadot - Network of Networks

Polkadot - Infrastructure for Infrastructure

Polkadot - Application Platform

Kusama - Canary Network of Polkadot

Kusama - Expect Chaos!

- + \ No newline at end of file diff --git a/docs/Polkadot/Module1/polkadotvision.html b/docs/Polkadot/Module1/polkadotvision.html index e8912faf9..fb7f46478 100644 --- a/docs/Polkadot/Module1/polkadotvision.html +++ b/docs/Polkadot/Module1/polkadotvision.html @@ -5,7 +5,7 @@ Vision of Polkadot | Polkadot Education Initiative - + @@ -26,7 +26,7 @@ otherwise each will become isolated and not adopted by as many users. Having to build such an interoperability mechanism introduces new challenges, many of which are missing in the centralised model because of the fundamental differences in the trust model between the two paradigms.

Automating Trust through Blockchain Technology

Decentralization efforts of Polkadot

Fostering Innovation on Polkadot

Overview of Polkadot Upgradeability

Polkadot Roadmap in 2018

Web3 and Future of the Internet

- + \ No newline at end of file diff --git a/docs/Polkadot/Module2/account.html b/docs/Polkadot/Module2/account.html index 64d9394c6..de08498d1 100644 --- a/docs/Polkadot/Module2/account.html +++ b/docs/Polkadot/Module2/account.html @@ -5,13 +5,13 @@ Create an Account | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module2/dotutility.html b/docs/Polkadot/Module2/dotutility.html index 0b5e18b00..525f21c92 100644 --- a/docs/Polkadot/Module2/dotutility.html +++ b/docs/Polkadot/Module2/dotutility.html @@ -5,7 +5,7 @@ DOT Token Utility | Polkadot Education Initiative - + @@ -17,7 +17,7 @@ backings are evenly distributed. On a more granular level, we pay or slash validators on a per- executed-action basis, and extend the same rewards or punishment onto nominators proportionally, to ensure that the rational strategy is compatible with honest behaviour.

What can you do with DOT?

ICOs vs Crowdloans

Crowdloans on Polkadot

- + \ No newline at end of file diff --git a/docs/Polkadot/Module2/explorenetwork.html b/docs/Polkadot/Module2/explorenetwork.html index 6b7452d75..7701aef86 100644 --- a/docs/Polkadot/Module2/explorenetwork.html +++ b/docs/Polkadot/Module2/explorenetwork.html @@ -5,7 +5,7 @@ Exploring Polkadot Network | Polkadot Education Initiative - + @@ -18,17 +18,17 @@ of state transition governed by transactions grouped in the relay chain blocks.

State

The state is represented through the use of an associative array data structure composed by a collection of (key; value) pairs where each key is unique. There is no assumption on the format of the key or the value stored under it besides the fact that they both the key and the value -need to be  nite byte arrays. +need to be finite byte arrays. The (key; value) pairs which comprise the relay chain state are arranged in a Merkle radix-16 -tree. The root of this tree canonically identi es the current state of the relay chain. The Merkle -tree also provides an e cient mean to produce the proof of inclusion for an individual pair in the +tree. The root of this tree canonically identifies the current state of the relay chain. The Merkle +tree also provides an efficient mean to produce the proof of inclusion for an individual pair in the state. To keep the state size in control, the relay chain state is solely used to facilitate the relay chain operations such as staking and identifying validators. The Merkle Radix tree is not supposed to store any information regarding the internal operation of the parachains.

State Transitions

Like any transaction-based transition system, Polkadot state changes via an executing ordered set of instructions, known as extrinsics. These extrinsics include transactions submitted by the public. They cover any data provided from \outside" of the machine's state which -can a ect state transition. Polkadot relay chain is divided into two major components, namely the +can affect state transition. Polkadot relay chain is divided into two major components, namely the \Runtime" and the \Runtime environment". The execution logic of the state-transition function is mainly encapsulated in the Runtime while all other generic operations, commonly shared among modern blockchain-based replicated state machines, are embedded into the Runtime environment. @@ -54,17 +54,17 @@ differentiation is used to reflect the different costs that a transaction incurs on the network and on the state, and to encourage the processing of certain types of transactions over others. A fraction of every transaction fee is paid to the block producer, while another fraction goes to - nance the Treasury. We highlight that, for a block producer, the rewards coming +finance the Treasury. We highlight that, for a block producer, the rewards coming from transaction fees may constitute only a small fraction of their overall revenue, just enough to incentivise inclusion on the block. We also run an adaptive transaction fee schedule that reacts to the traffic level, and ensures -that blocks are typically far from full, so that peaks of activity can be dealt with e ectively and +that blocks are typically far from full, so that peaks of activity can be dealt with effectively and long inclusion times are rare. In particular, the fee of each transaction is multiplied by a parameter that evolves over time depending on the current network traffic. We make fees evolve slowly enough, so that the fee of any transaction can be predicted accu- rately within a frame of an hour. In particular, we do not intend for transaction fees to be the -main source of income for stakers.

- +main source of income for stakers.

+ \ No newline at end of file diff --git a/docs/Polkadot/Module2/governance.html b/docs/Polkadot/Module2/governance.html index 6ce3740a9..230f6d47b 100644 --- a/docs/Polkadot/Module2/governance.html +++ b/docs/Polkadot/Module2/governance.html @@ -5,7 +5,7 @@ On-Chain Governance | Polkadot Education Initiative - + @@ -34,11 +34,11 @@ thing as little as, say, nudging the block time down by 5%. However, without this rule the network would likely be unstable, as placing its control outside of the hands of stakeholders would create a misalignment that may lead to inaction or worse. However, by taking advantage of the fact that -turnout is rarely 100%, we can e ect di erent outcomes depending on the circumstances, crafting +turnout is rarely 100%, we can effect different outcomes depending on the circumstances, crafting a balance of power between active and passive stakeholders. For example, simple voting systems typically introduce a notion of quorum, whereby a minimum amount of turnout must be reached -before a change is passed.

- +before a change is passed.

+ \ No newline at end of file diff --git a/docs/Polkadot/Module2/treasury.html b/docs/Polkadot/Module2/treasury.html index 2b35c5d4b..64017cdf1 100644 --- a/docs/Polkadot/Module2/treasury.html +++ b/docs/Polkadot/Module2/treasury.html @@ -5,7 +5,7 @@ Treasury | Polkadot Education Initiative - + @@ -17,7 +17,7 @@ and it will be the community and their collective imagination and judgment which really determines the course of the Treasury. Funds for Treasury are raised in two ways:

  1. by channeling some of the validator rewards that come from minting of new tokens, and
  2. by channeling a fraction of transaction fees and of slashings. -The  rst method allows us to maintain a  xed in +The first method allows us to maintain a fixed in ation rate while simultaneously having the validator rewards be dependent of the staking level: the difference between the scheduled minted tokens and the validator rewards is assigned to Treasury in each era. We also @@ -25,8 +25,8 @@ event that produced heavy stake slashing, the system is likely to need additional funds to develop software updates or new infrastructure that deal with an existing issue, or it might be decided by Governance to reimburse some of the slashed stake. Thus, it makes sense to have the slashed DOTs -available in Treasury, instead of burning them and having to mint more DOTs soon thereafter.

Polkadot Treasury Overview

Treasury Proposals and Bounties

Polkadot Treasury Payouts

Kusama Treasury

- +available in Treasury, instead of burning them and having to mint more DOTs soon thereafter.

Polkadot Treasury Overview

Treasury Proposals and Bounties

Polkadot Treasury Payouts

Kusama Treasury

+ \ No newline at end of file diff --git a/docs/Polkadot/Module3/consensus.html b/docs/Polkadot/Module3/consensus.html index cf2ea188a..09391efe5 100644 --- a/docs/Polkadot/Module3/consensus.html +++ b/docs/Polkadot/Module3/consensus.html @@ -5,7 +5,7 @@ Polkadot Network Consensus | Polkadot Education Initiative - + @@ -14,12 +14,12 @@ that all parachains are correct. This means that we need to be able to reorganise the chain and for that the chain needs to be capable of forking. Thus we use a block production mechanism, BABE, that while run by validators, has similar properties to proof-of-work chains. -Speci cally, we can use the longest chain rule as part of our consensus, and the next block producer +Specifically, we can use the longest chain rule as part of our consensus, and the next block producer is not known in advance. On its own BABE would require us to wait a long time from the moment a block is produced to the moment it is finalised, i.e. when we can be confident that with high -probability the block will never be reverted. Slow  finality is required in some circumstances to +probability the block will never be reverted. Slow finality is required in some circumstances to deal with challenges to availability. Most of the time, however, we would prefer to finalise blocks -much faster. For this purpose, validators  nalise blocks using GRANDPA, a finality +much faster. For this purpose, validators finalise blocks using GRANDPA, a finality gadget that is cleanly separated from block production. This separation makes it very adaptive and here allows us to delay finalising blocks until challenges are dealt with, without slowing down block production. GRANDPA gets Byzantine agreement on finalised blocks and will allow us to @@ -31,7 +31,7 @@ assignments are completely private until the assigned validators produce their blocks. Therefore, we use \Blind Assignment" in the protocol name. BABE is similar to Ouroboros Praos with some significant differences in the chain selection rule and timing assumptions. -In BABE, we may have slots without any assignment which we call empty slot. In order to  ll +In BABE, we may have slots without any assignment which we call empty slot. In order to fill the empty slots, we have a secondary block production mechanism based on Aura that assigns validators to slots publicly. We note that these blocks do not contribute to the security of BABE since the best chain selection and the random number generation algorithms work as if Aura blocks @@ -39,28 +39,28 @@ is to produce a block for the consistency and the security of BABE. For this, validators uses their local computer clock which is not adjusted by any centralized clock adjustment protocols such as the Network Time Protocol. Instead, they keep their clock synchronised with the other -validators with the relative time protocol.

In BABE, we assume that after the genesis block is released, elected validators of the  rst epoch +validators with the relative time protocol.

In BABE, we assume that after the genesis block is released, elected validators of the first epoch store the arrival time of the genesis block with respect to their local clock. Then, they mark the -beginning time of the  rst slot and increment the slot number every T seconds. After this point, +beginning time of the first slot and increment the slot number every T seconds. After this point, they periodically run the relative algorithm not to lose the synchronisation with others because of their local clock drifts. In addition to this, a validator who joins after the genesis block runs the -relative time algorithm to be synchronised with the other validators.

As mentioned above, we want a  nalisation mechanism that is +relative time algorithm to be synchronised with the other validators.

As mentioned above, we want a finalisation mechanism that is exible and separated from block -production, which is achieved by GRANDPA. The only modi cation to BABE required for it to +production, which is achieved by GRANDPA. The only modification to BABE required for it to work with GRANDPA is to change the fork-choice rule: instead of building on the longest chain, a validator producing a block should build on the longest chain including all blocks that it sees as - nalised. GRANDPA can work with many di erent block production mechanisms and it will be +finalised. GRANDPA can work with many different block production mechanisms and it will be possible to switch out BABE with another. Intuitively GRANDPA is a Byzantine agreement protocol that works to agree on a chain, out of many possible forks, by following some simpler fork choice rule, which together with the -block production mechanism would give probabilistic  nality if GRANDPA itself stopped  nalising +block production mechanism would give probabilistic finality if GRANDPA itself stopped finalising blocks. We want to be able to agree on many blocks at once, in contrast to single-block Byzantine agreement protocols. We assume that we can ask the fork choice rule for the best block given a particular block. The -basic idea is that we want to reach Byzantine agreement on the pre x of the chain that everyone -agrees on. To make this more robust, we try to agree on the pre x of the chain that 2/3 of -validators agree on.

GRANDPA Finality Gadget

- +basic idea is that we want to reach Byzantine agreement on the prefix of the chain that everyone +agrees on. To make this more robust, we try to agree on the prefix of the chain that 2/3 of +validators agree on.

GRANDPA Finality Gadget

+ \ No newline at end of file diff --git a/docs/Polkadot/Module3/maintainers.html b/docs/Polkadot/Module3/maintainers.html index cfa41aa98..4617f529b 100644 --- a/docs/Polkadot/Module3/maintainers.html +++ b/docs/Polkadot/Module3/maintainers.html @@ -5,7 +5,7 @@ Validators and Nominators | Polkadot Education Initiative - + @@ -14,8 +14,8 @@ here. Some of these roles have restrictions and conditions associated with them:

  • Validator: performs the bulk of the security work. Must be a full node of the relay chain. Interacts with parachain collators, but need not participate in a parachain as a full node.

  • Nominator: stakeholder who backs and selects validator candidates. This can be done from a light client, and they need not have any awareness of parachains.

  • Collator: collects and submits parachain data to the relay chain, subject to protocol rules -described below. They are chosen as de ned by the parachain, and must be full nodes of it.

Validators

A validator is the highest in charge and helps seal new blocks on the Polkadot -network. The validators role is contingent upon a su ciently high bond being deposited, though +described below. They are chosen as defined by the parachain, and must be full nodes of it.

Validators

A validator is the highest in charge and helps seal new blocks on the Polkadot +network. The validators role is contingent upon a sufficiently high bond being deposited, though we allow other bonded parties to nominate one or more validators to act for them and as such some portion of the validators bond may not necessarily be owned by the validator itself but rather by these nominators. A validator must run a relay-chain client implementation with high @@ -25,11 +25,11 @@ random and changes frequently. Since the validator cannot reasonably be expected to maintain a fully-synchronised database of all parachains, the task of devising a suggested new parachain block will be delegated to a third-party, known as a collator. Once all new parachain blocks have been -properly rati ed by their appointed validator subgroups, validators must then ratify the relay-chain +properly ratified by their appointed validator subgroups, validators must then ratify the relay-chain block itself. This involves updating the state of the transaction queues (essentially moving data from a parachains output queue to another parachains input queue), processing the transactions of -the rati ed relay-chain transaction set and ratifying the  nal block, including the  nal parachain -changes. A validator provably not ful lling their role will be slashed i.e. part or all of their bond +the ratified relay-chain transaction set and ratifying the final block, including the final parachain +changes. A validator provably not fulfilling their role will be slashed i.e. part or all of their bond will be taken. In some sense, validators are similar to the mining pools of current PoW blockchains.

Nominators

A nominator is a stake-holding party who contributes to the security bond of a validator. They have no additional role except to place risk capital and as such to signal that they trust a particular validator (or set thereof) to act responsibly in their maintenance of the network. @@ -41,8 +41,8 @@ much the same way as block producers do on current blockchains. Under normal circumstances, they will collate and execute transactions to create an unsealed block, and provide it, together with a proof of validity, to one or more validators presently responsible for proposing a parachain -block.

- +block.

+ \ No newline at end of file diff --git a/docs/Polkadot/Module3/npos.html b/docs/Polkadot/Module3/npos.html index 61ae13ae1..05e32aea6 100644 --- a/docs/Polkadot/Module3/npos.html +++ b/docs/Polkadot/Module3/npos.html @@ -5,7 +5,7 @@ Nominated Proof of Staking | Polkadot Education Initiative - + @@ -46,12 +46,11 @@ decentralised community, and it is important to engage all minorities in decision-making processes to ensure user satisfaction.

The goal of designing an electoral system that achieves proportional representation has been present in the literature for a very long time. Of special note is the work of Scandinavian math- -ematicians Edvard Phragm en and Thorvald Thiele in the late nineteenth century. Very recently, -there has been considerable e ort in the research community to formalise the notion of proportional -representation, and revisit the methods by Phragm en and Thiele and optimise them algorithmi- -cally. Our validator selection protocol is an adaptation of Phragm en's methods and is guaranteed -to observe the technical property of proportional justified representation (PJR)

- +ematicians Edvard Phragmén and Thorvald Thiele in the late nineteenth century. Very recently, +there has been considerable effort in the research community to formalise the notion of proportional +representation, and revisit the methods by Phragmén and Thiele and optimise them algorithmically. Our validator selection protocol is an adaptation of Phragmén's methods and is guaranteed +to observe the technical property of proportional justified representation (PJR)

+ \ No newline at end of file diff --git a/docs/Polkadot/Module3/securityimprovements.html b/docs/Polkadot/Module3/securityimprovements.html index b03b86a96..4fc5694d0 100644 --- a/docs/Polkadot/Module3/securityimprovements.html +++ b/docs/Polkadot/Module3/securityimprovements.html @@ -5,13 +5,13 @@ Security and Consensus Improvements | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module3/sharedsecurity.html b/docs/Polkadot/Module3/sharedsecurity.html index f991dbf40..acd7891a5 100644 --- a/docs/Polkadot/Module3/sharedsecurity.html +++ b/docs/Polkadot/Module3/sharedsecurity.html @@ -5,7 +5,7 @@ Securing the Network | Polkadot Education Initiative - + @@ -22,7 +22,7 @@ existence of some honest collators.

Parts of the protocol assume that every parachain has at least one reachable honest member; where this is not feasible or not realistic, we do not follow through on this assumption and instead have additional checks against a totally-malicious membership.

Block Availability and Validity

Parachain Block Data Availability

Blockchain Data Availability through Erasure Coding

Shared Security - Ethereum vs Polkadot vs Cosmos

- + \ No newline at end of file diff --git a/docs/Polkadot/Module4/cryptography.html b/docs/Polkadot/Module4/cryptography.html index 568531968..f5455644f 100644 --- a/docs/Polkadot/Module4/cryptography.html +++ b/docs/Polkadot/Module4/cryptography.html @@ -5,29 +5,29 @@ Cryptography | Polkadot Education Initiative - +

Cryptography

We assume that malicious parties generate their keys with an arbitrary algorithm while -honest ones always generate their keys securely.

In Polkadot, we necessarily distinguish among di erent permissions and functionalities with dif- +honest ones always generate their keys securely.

In Polkadot, we necessarily distinguish among different permissions and functionalities with dif- ferent keys and key types, respectively. We roughly categorise these into account keys with which users interact and session keys that nodes manage without operator intervention beyond a certificcation process.

Account Keys

Account keys have an associated balance of which portions can be locked to play roles in staking, resource rental, and governance, including waiting out a couple types of unlocking period. We -allow several locks of varying duration, both because these roles impose di erent restrictions, and +allow several locks of varying duration, both because these roles impose different restrictions, and for multiple unlocking periods running concurrently. We encourage active participation in all these roles, but they all require occasional signatures from accounts. At the same time, account keys have better physical security when kept in incon- venient locations, like safety deposit boxes, which makes signing arduous. We avoid this friction for users as follows. Accounts that lock funds for staking are called stash accounts. All stash accounts register a -certi cate on-chain that delegates all validator operation and nomination powers to some controller +certificate on-chain that delegates all validator operation and nomination powers to some controller account, and also designates some proxy key for governance votes. In this state, the controller and proxy accounts can sign for the stash account in staking and governance functions respectively, but not transfer funds. At present, we support both ed25519 and Schnorrkel/sr25519 for account keys. These -are both Schnorr-like signatures implemented using the Ed25519 curve, so both o er extremely +are both Schnorr-like signatures implemented using the Ed25519 curve, so both offer extremely similar security. We recommend ed25519 keys for users who require Hardware Security Module (HSM) support or other external key management solution, while Schnorrkel/sr25519 provides more blockchain-friendly functionality like Hierarchical Deterministic Key Derivation (HDKD) @@ -35,25 +35,25 @@ In particular, Schnorrkel/sr25519 uses the Ristretto implementation of Mike Hamburg's Decaf, which provide the 2-torsion free points of the Ed25519 curve as a prime order group. Avoiding the cofactor like this means Ristretto makes implementing more complex pro- -tocols signi cantly safer. We employ Blake2b for most conventional hashing in Polkadot, but +tocols significantly safer. We employ Blake2b for most conventional hashing in Polkadot, but Schnorrkel/sr25519 itself uses STROBE128, which is based on Keccak-f(1600) and provides a hashing interface well suited to signatures and non-interactive zero-knowledge proofs (NIZKs).

Session Keys

Session keys each fill roughly one particular role in consensus or security. As a rule, session keys gain -authority only from a session certi cate, signed by some controller key, that delegates appropriate +authority only from a session certificate, signed by some controller key, that delegates appropriate stake. -At any time, the controller key can pause or revoke this session certi cate and/or issue re- +At any time, the controller key can pause or revoke this session certificate and/or issue re- placement with new session keys. All new session keys can be registered in advance, and most -must be, so validators can cleanly transition to new hardware by issuing session certi cates that +must be, so validators can cleanly transition to new hardware by issuing session certificates that only become valid after some future session. We suggest using pause mechanism for emergency maintenance and using revocation if a session key might be compromised. We prefer if session keys remain tied to one physical machine because doing so minimises the -risk of accidental equivocation. We ask validator operators to issue session certi cates using an +risk of accidental equivocation. We ask validator operators to issue session certificates using an RPC protocol, not to handle the session secret keys themselves. Almost all early proof-of-stake networks have a negligent public key infrastructure that en- courages duplicating session secret keys across machines, and thus reduces security and leads to pointless slashing. We impose no prior restrictions on the cryptography employed by specific components or their -associated session keys types

- +associated session keys types

+ \ No newline at end of file diff --git a/docs/Polkadot/Module4/decentralization.html b/docs/Polkadot/Module4/decentralization.html index 1ac930484..622586461 100644 --- a/docs/Polkadot/Module4/decentralization.html +++ b/docs/Polkadot/Module4/decentralization.html @@ -5,7 +5,7 @@ Decentralization of Network | Polkadot Education Initiative - + @@ -17,14 +17,14 @@ possibly malicious but no consideration is given to malicious edges. A security requirement like

1=3 of nodes are honest in the model, in fact translates to > 1=3 of nodes are honest and can all communicate perfectly reliably with each other all the time in reality. Conversely, if an edge is controlled by a malicious ISP in reality, it is the corresponding node(s) that must be treated as -malicious in any analysis under the model. More signi cantly, if the underlying communications +malicious in any analysis under the model. More significantly, if the underlying communications network is centralised, this can give the central parties the ability to corrupt > 1=3 of nodes within the model thereby breaking its security assumptions, even if they don't actually have arbitrary execution rights on that many nodes. In this section we outline and enumerate the communication primitives that we require in Polkadot, and sketch a high-level design on how we achieve these in a decentralised way, with the -specifics to be re ned as we move forward with a production system.

Why Decentralize?

Polkadot Decentralization Efforts

- +specifics to be refined as we move forward with a production system.

Why Decentralize?

Polkadot Decentralization Efforts

+ \ No newline at end of file diff --git a/docs/Polkadot/Module4/networking.html b/docs/Polkadot/Module4/networking.html index 6a97db008..356c91ae3 100644 --- a/docs/Polkadot/Module4/networking.html +++ b/docs/Polkadot/Module4/networking.html @@ -5,7 +5,7 @@ Networking | Polkadot Education Initiative - + @@ -17,21 +17,19 @@ eventual delivery of a message in Polkadot.

Polkadot's networking needs to extend the peer-to-peer gossip network that is standard in single chain permissionless blockchains to a multi-chain system, where any nodes network traffic should not scale with the total data of the system.

Gossipping

This subprotocol is used for most relay-chain artefacts, where everyone needs to see more-or-less -the same public information. Part of its structure is also used for when a node goes o ine for a +the same public information. Part of its structure is also used for when a node goes offline for a long time and needs to synchronise any newer data it hasn't seen before. The Polkadot relay chain network forms a gossip overlay network on top of the physical commu- -nications network, as an e cient way to provide a decentralised broadcast medium. The network +nications network, as an efficient way to provide a decentralised broadcast medium. The network consists of a known number of trusted nodes (validators) who have been permissioned via staking, and an unknown number of untrusted nodes (full nodes that don't perform validation) from the permissionless open internet. (As an aside, recall that some of the untrusted nodes may have other -roles as de ned earlier, e.g. parachain collator,  shermen, etc.) +roles as defined earlier, e.g. parachain collator, fishermen, etc.) A simple push-based approach is implemented currently, with hash-based tracker caches to -avoid sending duplicates to peers, and a few restrictions to avoid the most common spam attacks: -  Artefacts may only be received in dependency order; peers are not allowed to send them out- -of-order. Though this decreases network-level e ciency, it is straightforward to implement -and provides a healthy level of security.

  To e ciently communicate to sending peers what they are allowed to send in dependency -order, periodically peers update each other with their view of the latest heads of the chain. -There are also more speci c constraint rules applied to artefacts belonging to the various higher- +avoid sending duplicates to peers, and a few restrictions to avoid the most common spam attacks:

  • Artefacts may only be received in dependency order; peers are not allowed to send them out- +of-order. Though this decreases network-level efficiency, it is straightforward to implement +and provides a healthy level of security.

  • To efficiently communicate to sending peers what they are allowed to send in dependency +order, periodically peers update each other with their view of the latest heads of the chain.

There are also more specific constraint rules applied to artefacts belonging to the various higher- level subprotocols using the gossip protocol, to avoid broadcasting obsolete or otherwise unneeded artefacts. For example, for GRANDPA we only allow two votes being received for each type of vote, round number, and voter; any further votes will be ignored. For block production only valid @@ -42,19 +40,19 @@ basis by performing random lookups in the address book. Further work will proceed along two fronts:

  1. Trusted nodes will reserve a portion of their bandwidth and connection resources, to form a structured overlay with a deterministic but unpredictable topology that rotates every era. -For nodes running behind sentries, this e ectively means that their sentry nodes instead +For nodes running behind sentries, this effectively means that their sentry nodes instead participate in this topology.
  2. For the remainder of trusted nodes' resource capacity, and for the whole of untrusted nodes' resource capacity, they will select neighbours via a scheme based on latency measurements, with the details to be decided. Notably, for good security properties we want a scheme that -does not simply choose "closest  rst", but also some far links as well. +does not simply choose "closest first", but also some far links as well. In some sense, this can be viewed as the trusted nodes forming a core with the untrusted nodes around it - but note that trusted nodes are expected to use some of their resources to serve untrusted nodes as well. Both topologies are chosen to mitigate eclipse attacks, as well as sybil attacks in the permissionless untrusted case. Further work will also include some sort of set reconciliation protocol, to further reduce re- dundancy when many senders attempt to send the same object to the same recipient at once; and -potentially look into lifting the dependency-order restriction whilst retaining security.

Networking Complexity

- +potentially look into lifting the dependency-order restriction whilst retaining security.

Networking Complexity

+ \ No newline at end of file diff --git a/docs/Polkadot/Module4/nodes.html b/docs/Polkadot/Module4/nodes.html index 92f5b20c4..0a7962791 100644 --- a/docs/Polkadot/Module4/nodes.html +++ b/docs/Polkadot/Module4/nodes.html @@ -5,7 +5,7 @@ Nodes on Polkadot Network | Polkadot Education Initiative - + @@ -17,7 +17,7 @@ as any of these types of nodes:

  • Light client: retrieves certain user-relevant data from the network. The availability of light clients is irrelevant. They don't perform a service for others.
  • Full node: retrieves all types of data, stores it long-term, and propagates it to others. Must be highly available.

RPCs and Light Nodes

- + \ No newline at end of file diff --git a/docs/Polkadot/Module4/parachainblock.html b/docs/Polkadot/Module4/parachainblock.html index f2d57bac9..40adb129e 100644 --- a/docs/Polkadot/Module4/parachainblock.html +++ b/docs/Polkadot/Module4/parachainblock.html @@ -5,14 +5,14 @@ Path of a Parachain Block | Polkadot Education Initiative - +

Path of a Parachain Block

Collators watch the progress of the block-producing and consensus protocols, e.g. by participating in the relay chain as a full node. Based on what they think is the latest relay chain block that will -most likely be  nalised, they build on top of the latest parachain block (or other data) that would be   +most likely be finalised, they build on top of the latest parachain block (or other data) that would be finalised by it.

Collators sign data building on top of said latest parachain block, and submit it possibly indirectly, to the validators assigned to their parachain (parachain validators for short), for inclusion in the relay chain. Ideally they submit a unique one, to help performance.

The parachain validators decide which parachain block to support, and presents relevant @@ -25,12 +25,12 @@ it to various other relay-chain nodes.

Data submitted from a parachain might include indications that they are sending messages to another parachain, including metadata to facilitate this. This is now included on the relay chain head(s), so recipient parachains are aware of which new messages have been sent to -them. They now retrieve the message bodies from the sending parachains.

Validators submit their votes on the block and  nalises it, resolving any forks to a single +them. They now retrieve the message bodies from the sending parachains.

Validators submit their votes on the block and finalises it, resolving any forks to a single head. These votes are added to the relay chain blocks.

Parachains in Action

In outline, a collator produces a parachain block, sends it to the parachain validators, who sign its header as valid, and the header with enough signatures is placed on the relay chain. At this point, the parachain block is as canonical as the relay chain block its header appeared in. If this relay chain block is in the best chain according to BABE, so is the parachain -block and when this relay chain block is  nalised, so is the parachain block. +block and when this relay chain block is finalised, so is the parachain block. Because the parachain validators switch parachains frequently, they are stateless clients of the parachain. Thus we distinguish between the parachain block B, which is normally enough for full nodes of the parachain such as collators to update the parachain state, and the Proof of @@ -46,16 +46,16 @@ itself doing so. The PoV block will be the parachain block, its outgoing messages, its header and light client proof witnesses. These witnesses are Merkle proofs that give all elements of the input and output -state that are used or modi ed by the state transition from the input and output state roots. +state that are used or modified by the state transition from the input and output state roots. To aid in censorship resistance, a parachain may want to use proof of work or proof of stake to select collators, where the selection strategy is up to the given parachain. This can be implemented in the STVF and need not be a part of the Polkadot protocol. So for proof of work, the STVF would -check that the hash of the block is su ciently small. However, for speed, it would be useful to +check that the hash of the block is sufficiently small. However, for speed, it would be useful to ensure that most relay chain blocks can include a parachain block. For PoW, this would necessitate it being probable that multiple collators are allowed to produce a block. As such we will still need a tie-breaker for the parachain validators to coordinate on validating the same parachain block - rst. This may be the golden ticket scheme of. For proof of stake this may not be necessary.

- +first. This may be the golden ticket scheme of. For proof of stake this may not be necessary.

+ \ No newline at end of file diff --git a/docs/Polkadot/Module5/architectureimprovements.html b/docs/Polkadot/Module5/architectureimprovements.html index 337208b84..d96613963 100644 --- a/docs/Polkadot/Module5/architectureimprovements.html +++ b/docs/Polkadot/Module5/architectureimprovements.html @@ -5,7 +5,7 @@ Polkadot Architecture Improvements | Polkadot Education Initiative - + @@ -19,7 +19,7 @@ centrally and the execution of that code and its storage will be sandboxed from the rest of the state transition. This ensures that this token transfer message is interpreted correctly and obtains the guarantees about tokens we want.

- + \ No newline at end of file diff --git a/docs/Polkadot/Module5/bridges.html b/docs/Polkadot/Module5/bridges.html index ddf07552c..cc9019e15 100644 --- a/docs/Polkadot/Module5/bridges.html +++ b/docs/Polkadot/Module5/bridges.html @@ -5,13 +5,13 @@ Polkadot Bridges | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module5/interoperability.html b/docs/Polkadot/Module5/interoperability.html index ff265a450..9a8dc725f 100644 --- a/docs/Polkadot/Module5/interoperability.html +++ b/docs/Polkadot/Module5/interoperability.html @@ -5,7 +5,7 @@ Interoperability | Polkadot Education Initiative - + @@ -18,11 +18,11 @@ receive messages that were sent by blocks recorded on this new relay chain fork, and not the reverted fork. Thus we need that the parachain and XCMP logic ensure that a fork of the relay chain defines a consistent history of Polkadot and thus messages only arrive when they have been -sent previously in the history de ned by this fork.

XCMP is the protocol that parachains use to send messages to each other. It aims to guarantee -four things:  rst that messages arrive quickly; second that messages from one parachain arrive to +sent previously in the history defined by this fork.

XCMP is the protocol that parachains use to send messages to each other. It aims to guarantee +four things: first that messages arrive quickly; second that messages from one parachain arrive to another in order; third that arriving messages were indeed sent in the finalised history of the sending chain; and fourth that recipients will receive messages fairly across senders, helping guarantee that -senders never wait inde nitely for their messages to be seen. +senders never wait indefinitely for their messages to be seen. There are two parts to XCMP. (1) Metadata about outgoing messages for a parachain block are included on the relay chain and later this metadata is used to authenticate messages by the receiving parachain. (2) The message bodies corresponding to this metadata need to be actually @@ -33,14 +33,14 @@ of time for parachain blocks, just by relay chain block numbers. Additionally it allows us to authenticate messages as being sent in the history given by the relay chain i.e. it is impossible that one parachain sends a message, then reorgs 2 so that that message was not sent, but has -been received. This holds even though the system may not have reached  nality over whether the +been received. This holds even though the system may not have reached finality over whether the message was sent, because any relay chain provides a consistent history. Because we require parachains to act on every message eventually, non-delivery of a single message can potentially stop a parachain from being able to build blocks. Consequently we need enough redundancy in our message delivery system. Any validators who validate the PoV block should keep any outgoing messages from that block available for a day or so and all full nodes of -the sending parachain also store the outgoing messages until they know they have been acted on.

- +the sending parachain also store the outgoing messages until they know they have been acted on.

+ \ No newline at end of file diff --git a/docs/Polkadot/Module5/parachains.html b/docs/Polkadot/Module5/parachains.html index 00f7051ae..717834179 100644 --- a/docs/Polkadot/Module5/parachains.html +++ b/docs/Polkadot/Module5/parachains.html @@ -5,13 +5,13 @@ Parachains | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module5/scalability.html b/docs/Polkadot/Module5/scalability.html index 29411aef7..ac8f6c2f0 100644 --- a/docs/Polkadot/Module5/scalability.html +++ b/docs/Polkadot/Module5/scalability.html @@ -5,7 +5,7 @@ Scalability | Polkadot Education Initiative - + @@ -14,9 +14,9 @@ block and set of outgoing messages from the parachain is available for a while. The naive solution for this would be broadcasting/gossip the parachain blobs to all relay chain nodes, which is not a feasible option because there are many parachains and the PoV blocks may be big. We want -to  nd an e cient solution to ensure PoV blocks from any recently created parachain blocks are +to find an efficient solution to ensure PoV blocks from any recently created parachain blocks are available.

For a single chain, such as Bitcoin, as long as 51% of hash power is honest, not making block data -available ensures that no honest miner builds on it so it will not be in the  nal chain. However, +available ensures that no honest miner builds on it so it will not be in the final chain. However, parachain consensus in Polkadot is determined by relay chain consensus. A parachain block is canonical when its header is in the relay chain. We have no guarantees that anyone other than the collator and parachain validators have seen the PoV block. If these collude then the rest of the @@ -27,25 +27,25 @@ distribute the PoV block to all validators. When any misbehaviour, particularly in relation to invalidity, is detected, the blob can be reconstructed from the distributed erasure coded pieces. If a block is available then full nodes of the parachain, and any light client that has the PoV -block, can check its validity. We have three-level of validity checks in Polkadot. The  rst validity +block, can check its validity. We have three-level of validity checks in Polkadot. The first validity check of a PoV block is executed by the corresponding parachain validators. If they verify the PoV block then they sign and distribute the erasure codes of the blob, including the PoV block, to -each validator. We rely on nodes acting as  shermen to report the invalidity of a blob as a second +each validator. We rely on nodes acting as fishermen to report the invalidity of a blob as a second level of validity checking. They would need to back any claim with their own stake in DOTs. We -would assume that most collators will be  shermen, as they have a stake in continued validity of +would assume that most collators will be fishermen, as they have a stake in continued validity of the chain and are already running full nodes, so all they need is stake in DOTs. The third level of validity checking is executed by a few randomly and privately assigned validators. We determine the number of validators in the third level of validity checking considering the amount of invalidity -reports given by  shermen and unavailability reports given by collators. If an invalid parachain +reports given by fishermen and unavailability reports given by collators. If an invalid parachain block is detected, the validators who signed for its validity are slashed. We wait for enough of these randomly assigned checkers to check the block before voting on it in GRANDPA. We also want to ensure that the block is available before selecting the randomly assigned validators. This means that the parachain validators have to commit running a high risk of being slashed for a small -probability of getting an invalid block  nalised. This means that the expected cost of getting an +probability of getting an invalid block finalised. This means that the expected cost of getting an invalid block into Polkadot is higher than the amount of stake backing a single parachain. The security of our availability and validity scheme is based on the security of the GRANDPA - nality gadget and the quality of randomness generated in each BABE epoch.

- +finality gadget and the quality of randomness generated in each BABE epoch.

+ \ No newline at end of file diff --git a/docs/Polkadot/Module6/developers.html b/docs/Polkadot/Module6/developers.html index 01aacc077..a70602731 100644 --- a/docs/Polkadot/Module6/developers.html +++ b/docs/Polkadot/Module6/developers.html @@ -5,13 +5,13 @@ Developer Community | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module6/polkadotjs.html b/docs/Polkadot/Module6/polkadotjs.html index a3501b6ed..257591ca7 100644 --- a/docs/Polkadot/Module6/polkadotjs.html +++ b/docs/Polkadot/Module6/polkadotjs.html @@ -5,13 +5,13 @@ Polkadot JS | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module6/rust.html b/docs/Polkadot/Module6/rust.html index e0cc5dff6..aa8055d65 100644 --- a/docs/Polkadot/Module6/rust.html +++ b/docs/Polkadot/Module6/rust.html @@ -5,13 +5,13 @@ Rust for Blockchain Development | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module6/substrate.html b/docs/Polkadot/Module6/substrate.html index 5eb5d8b53..130514540 100644 --- a/docs/Polkadot/Module6/substrate.html +++ b/docs/Polkadot/Module6/substrate.html @@ -5,13 +5,13 @@ Introduction to Substrate | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Polkadot/Module6/testnets.html b/docs/Polkadot/Module6/testnets.html index c403c5470..eb3189745 100644 --- a/docs/Polkadot/Module6/testnets.html +++ b/docs/Polkadot/Module6/testnets.html @@ -5,13 +5,13 @@ Polkadot Test Networks | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Rust/rust-appendix.html b/docs/Rust/rust-appendix.html index ced700fbb..d4aeec9f3 100644 --- a/docs/Rust/rust-appendix.html +++ b/docs/Rust/rust-appendix.html @@ -5,13 +5,13 @@ Course Appendix | Polkadot Education Initiative - +

Course Appendix

This page will cover parts that may seem ambiguous or not explained as explicitly as other topics (i.e., println!(), macros, etc.). This course is primarily based on the Rust Book, as well as parts of Rust by Example and the Rust Reference. Using these comprehensive and esteemed resources to solidify your learning further is highly encouraged.

Common Formatting & Macros

Throughout this course, the println!() macro will be used excessively, which prints some value to the screen. There are a few different ways this macro can be utilized.

  • The two curly brackets indicate a placeholder for some value. Those brackets are called the "format syntax", as they format data into an existing String. For example, println!("Hello, {}", "World") would print "Hello, World". Using this shortcut, most values that implement the Debug or Display trait can be displayed using println!(). Most primitive data types already implement this trait.
  • Sometimes, the format syntax {} may be used inline the quotes. For example, println!("{some_variable}") accomplishes the same as above but with a slightly shortened syntax.

Commonly Used Types

  • String - this type is not explained till Module 5 of this course. However, it is used beforehand for the sake of an example. The :: accessor is used to access some practical methods, as shown below:
    • String::from("Some text") - Used to create a new String from a string literal.
    • String::new() - Used to create a new, 'empty' instance of a String.
- + \ No newline at end of file diff --git a/docs/Rust/section1.html b/docs/Rust/section1.html index 42dcfd03d..a44ffdc35 100644 --- a/docs/Rust/section1.html +++ b/docs/Rust/section1.html @@ -5,13 +5,13 @@ Why Learn Rust? | Polkadot Education Initiative - +
- + \ No newline at end of file diff --git a/docs/Rust/section1/wasm-tldr.html b/docs/Rust/section1/wasm-tldr.html index 5bd360902..4e83f42d1 100644 --- a/docs/Rust/section1/wasm-tldr.html +++ b/docs/Rust/section1/wasm-tldr.html @@ -5,13 +5,13 @@ WASM TLDR - What is WASM, and why is it important? | Polkadot Education Initiative - +

WASM TLDR - What is WASM, and why is it important?

WebAssembly (Wasm) is becoming a core part of many technology stacks. It is a portable target for higher-level languages to compile to and has no direct limitations on where it can run (although it is mostly browser focused at the moment). It can run at near-native speeds and provides a sandboxed, agnostic set of instructions to run in any environment that supports it.

Wasm has two standard formats, .wat and .wasm.

  • .wat - a text-based format called WebAssembly Text, a more human-readable way of representing Wasm instructions.
  • .wasm - a binary-based format that defines an executable that is then executed by a Wasm virtual machine.

WebAssembly, while not exclusive to Rust by any means, is highly supported by Rust. This enables Rust code to be even more portable in web-based applications (or any environment that supports WebAssembly).

Forkless Upgrades, thanks to WASM

Heard of forkless upgrades on Polkadot? It is Wasm that enables these seamless blockchain upgrades. Each instance of the Polkadot Runtime compiles to a Wasm blob and is stored in the blockchain's storage, which can be replaced through an on-chain governance-based upgrade.

Basic Wasm Architecture

At its core, Wasm is a stack-based virtual machine where each value is pulled and pushed on an arbitrary stack. It is worth noting that Wasm is not a register machine, as it contains no context-specific registers for holding bytes.

Wasm Use cases

On Polkadot, Wasm is a core part of the technology stack:

  • It defines reliable state transition functions for the relay chain runtimes.
  • It defines Parachain Validation Functions (PVFs), which is a core part of how relay and parachains agree on their respective state.
  • The ink! smart contract language compiles to Wasm, which takes full advantage of Wasm’s sandboxed and portable nature.
- + \ No newline at end of file diff --git a/docs/Rust/section1/what-is-rust.html b/docs/Rust/section1/what-is-rust.html index 998696eb5..9ef368628 100644 --- a/docs/Rust/section1/what-is-rust.html +++ b/docs/Rust/section1/what-is-rust.html @@ -5,13 +5,13 @@ What is Rust? | Polkadot Education Initiative - +

What is Rust?

Rust is a systems programming language aimed to empower its users with high-level, ergonomic code while also maintaining a level of control akin to a language like the C programming language. It is meant to empower and give confidence to whoever is writing it and guarantee secure and reliable executable code.

It combines the best of object-oriented and functional programming concepts into one single language. Rust is statically typed, focuses on code and memory safety, and has a powerful memory management system that eliminates the need for a garbage collector.

Learning Rust for Substrate

In this course, we'll learn to program in the Rust programming language with a specific focus on the aspects of Rust that are used in the Substrate Blockchain framework. We do not assume any existing knowledge of Rust and will start from the beginning. However, we assume some general familiarity with programming and that you are fluent in at least one other programming language. If you are new to programming, starting with Rust is possible, but this course may be too fast-paced for a beginner.

Throughout the course, we will emphasize the differences and similarities between Rust and other programming languages you may have encountered. Design patterns used in the Substrate blockchain framework will also be highlighted, as well as general familiarity with coding techniques and patterns you will face when using Substrate.

This course will still be beneficial for someone who wants to learn Rust but has yet to intend to use Substrate.

Course Format

Each module in this course follows the format below:

  1. Background information about a particular topic
  2. Relevant code example
  3. Interactive portion made possible by the Rust Playground
  4. A "What is happening here?" with a line-by-line explanation of what the code is accomplishing

Here is an example of how that might look like:

"Hello World" in Rust

info

The classic "Hello, world" program in Rust.

The main function

In every Rust program comes the fn main() function. This function is the entry point for the program and is where code is executed when compiled and where the execution of the program begins. Every Rust program must have a main function. It takes no arguments and returns what is called a unit type in Rust: ().

note

The unit type, or (), is a placeholder value that is used when no meaningful or real value is applicable. All functions without a return type default to returning a unit type in Rust.

// Define entry point.
fn main() {

}

What does the program above do? Well, nothing. There is no executable code within the curly brackets of the main() function; however, it is still a valid Rust program.

Try it yourself!

This course is meant to be hands-on. Delete the // before println!("Hello world!");, and click run. Optionally, you may also change the value within the quotes and experiment.

What is happening here?

The code above defines a Rust program with a main function. As stated before, this function is required in every Rust program and signals where execution begins. println! is a Rust macro, as denoted by the ! affixed to the end of the statement. Rust macros will be covered later on; however, for now, know that println!() is used to print to the console.

Resources

- + \ No newline at end of file diff --git a/docs/Rust/section1/why-rust.html b/docs/Rust/section1/why-rust.html index 0a6c2f99a..29612cc35 100644 --- a/docs/Rust/section1/why-rust.html +++ b/docs/Rust/section1/why-rust.html @@ -5,13 +5,13 @@ Why Learn Rust? | Polkadot Education Initiative - +

Why Learn Rust?

Before starting your Rust development journey, it is essential to understand why Rust was the language chosen for this course as, if you are new to programming, the reasons why Rust was chosen may be foreign.

Before learning more about Rust, see below a few general programming terms and what they mean.

  • A programming language is a way for humans to write instructions for a computer to follow. Programming languages are exact and similar to spoken languages like English, Spanish, or French; and contain a set of rules for how a program should be written. This is called the syntax of the program.

  • A compiler is a special program that translates the code we write, whether it be Rust or something else, into something a computer can understand (machine code). It acts as a translator between the human code and the machine.

  • There are two phases of a program - compile-time and runtime. Compile time is when a human’s code is converted to machine code. Runtime refers to the period when the program has been running after it has been compiled.

  • A garbage collector manages a program’s memory, i.e. when and where memory should be allocated and released. The benefit of a garbage collector is that the programmer doesn't have to account for how to allocate memory within code.

Rust is safe

The primary reason why Rust is used is because of safety reasons. In the context of programming, safety means that the programmer can confidently write a program with the certainty that it will work as intended. In more technical terms, a safe language ensures memory safety and a safe language cannot write a dangerous program (unless those safety features are bypassed).

Safety is mainly derived from two factors that work in tandem:

  1. Rust's strict type system
  2. Rust's robust and pedantic compiler that checks every single line of code to ensure it will not be invalid now, or in the future.

For example, if the Rust compiler detects an issue with a variable within your program, it will notify the programmer that this would become an issue in runtime and the program will not be allowed to compile.

Safe programming is also a matter of security. Many exploits and hacks occur because the program's memory can be accessed and modified in an unsafe or unauthorized way.

These factors are crucial when building applications and programs that make guarantees in impactful industries. The Rust compiler's strictness makes it hard to write bad or dangerous code.

Safety

Because Rust is so strict at compile-time, there is no need for a garbage collector or any other mechanism that operates with the program’s runtime. Effectively, this means that Rust’s powerful, robust typing system comes at zero cost. These abstractions do not sacrifice the readability, ease of use, or speed of Rust.

This allows for a level of control comparable to something lower-level, such as C, but is much more beginner friendly in terms of writing useful programs. It also allows for flexibility for applications that require granular control and resource management, such as embedded systems, operating systems, and distributed ledgers.

Rust lowers the barriers when dealing with more complex concepts such as concurrency, low-level memory management, and data representation.

Rust Safety by Example

Take this example - if we try to compile this Rust code (note that the lines of code starting with // are comments that are not executed):

    // Here, the number five variable is, well, the number 5. It is a number that can be added and subtracted.
let the_number_five: u32 = 5;
// Here is another variable - but instead of being a number it's a character, as denoted by the `char` type and the single quotes surrounding the 5 ('5')
let imposter_number_five: char = '5';
// Let’s say we want to add them - this shouldn't work, as this is the same as trying to add a number to a word.
let the_number_ten: u32 = the_number_five + imposter_number_five;
// FAILURE!
println!("{the_number_ten}");

The Rust compiler, before we even run the program, gives an error as to why this is impossible to compile. See below the level of detail the compiler gives the programmer, including a reference for why this may not compile.

  Compiling playground v0.0.1 (/playground)
error[E0277]: cannot add `char` to `u32`
--> src/main.rs:5:38
|
5 | let the_number_ten = the_number_five + imposter_number_five;
| ^ no implementation for `u32 + char`
|
= help: the trait `Add<char>` is not implemented for `u32`
= help: the following other types implement trait `Add<Rhs>`:
<&'a u32 as Add<u32>>
<&u32 as Add<&u32>>
<u32 as Add<&u32>>
<u32 as Add>

For more information about this error, try `rustc --explain E0277`.

In this specific case we cannot add a type number with a character.

Resources

- + \ No newline at end of file diff --git a/docs/Rust/section2.html b/docs/Rust/section2.html index 0a4aceb75..5849d000f 100644 --- a/docs/Rust/section2.html +++ b/docs/Rust/section2.html @@ -5,13 +5,13 @@ Rust 101 - Intro to Basic Rust | Polkadot Education Initiative - +
- + \ No newline at end of file diff --git a/docs/Rust/section2/data-types.html b/docs/Rust/section2/data-types.html index 82a88a7e9..f343376a3 100644 --- a/docs/Rust/section2/data-types.html +++ b/docs/Rust/section2/data-types.html @@ -5,13 +5,13 @@ Data Types in Rust | Polkadot Education Initiative - +

Data Types in Rust

As mentioned before in the introduction of this course, one of Rust's main objectives is to have a robust, compile-time type system. Just as the immutability of variables helps with safety, having types in Rust greatly aids in ensuring that data is flowing as it should throughout the program.

Rust has two kinds of primitive, or base, data types:

  • Scalar - single point types, such as numbers and booleans (true or false statements)
  • Compound - arrays, and tuples

Scalar Types

Scalar types represent a single value, such as a number or boolean. Rust has four core Scalar types, which you have most likely seen in other programming languages:

  • Integers
  • Floating-point numbers
  • Booleans
  • Characters

Integers & Floating-point Types

An integer in Rust is the same as in mathematics - a non-fractional, whole number that can be either positive or negative. There are two types of integers: unsigned (positive numbers) and signed (negative or positive numbers).

  • Signed integers are denoted by the i, followed by the length of the number: i32.
  • Unsigned integers start with u, followed by the length of the number: u32.

The numeric characters 32 that follow whether an integer is signed or unsigned denotes the length of the number. Take this table from the Rust Book, which states all possible integer variants:

LengthSignedUnsigned
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
archisizeusize

The length or size of the integer is always explicitly declared. Rust has a set of defaults for inferring types, such as integers defaulting to the i32 type. usize and isize depend on your machine's architecture, meaning they are either 32, or 64 bit in size.

Here are some common ways to declare an integer, some formats less traditional than others:


// Defaults to i32.
let default = 10;

// Explicitly declare this variable as an unsigned, 64-bit integer:
let sixty_four_bit_int: u64 = 10;

// You can also declare integer literals like so:
let big_number = 65_550; // 65,550

// Another way to specify the type can be done with this syntax, with the type following the number:
let short_hand = 455u32;

// Declaring a hex literal is possible!
let hex = 0x001;

// You can also represent characters as unsigned 8-bit numbers. This will get very useful later on:
let word_as_bytes: u8 = b'F';

Lastly, as integers do have a size, integer overflow is possible if you exceed the limit of a given type. For example, the type u8 has a minimum of 0 and a maximum of 255, meaning 256 would cause a panic. As the Rust compiler doesn't check for overflows, there are some additional functions you can use to ensure that adding or subtracting is always safe and won't cause a runtime error. We'll learn more about those later on.

Floating point numbers

Rust has two floating number types: ' f32(32-bit) andf64` (64-bit). Floating point numbers, unlike integers, are fractional, meaning they contain decimal points to represent parts of whole numbers:

f32 has single precision accuracy, whereas f64 has double-precision accuracy. f64 is the default type for floating point numbers in Rust:


let floating_double: f64 = 1.11;

let floating_single: f32 = 1.4;

let also_floating_double = 1.45;

Operators

As in any other programming language, there are a set of operators that allow for basic mathematics to be performed:


let addition = 1 + 1; // 2
let subtraction = 10 - 5; // 5
let division = 8 / 4; // 2
let multiplication = 4 * 4; // 16

Likewise, Rust also supports bitwise operators. A full list of Rust's operators and symbols may be found here.

Boolean Types

A boolean value can only have two possible states: true or false. Declaring a boolean in Rust is simple:


let i_am_true = true;

// Usually, it's easy to infer a bool type; however good to show explicit type assignments.
let i_am_false: bool = false;

This opens up many possibilities for branching logic in our Rust programs, of which we'll cover later.

Character Type

The character, or char type in Rust is the basic building block for alphabetic values. It simply declares a single character, which is encased by single quotes. At its core, it represents a Unicode Scalar Value, supporting a plethora of characters as well as zero-width spaces:

// Both are valid Unicode characters!
let the_letter_a = 'A';
let the_moon = '🌒';

Compound Types

Scalar types define how variables can hold a single value at a time. Compound types can hold multiple values under the same, unifying type.

There are two primary kinds of compound types - tuples and arrays.

Tuples

A tuple is a way to combine multiple values and types into a single, compound type. It can be useful for describing a set of varying values which have some relationship to one another.

The syntax for writing a tuple is as follows. Note that the type annotations are optional:


let my_tuple: (u32, f32, char) = (5, 5.5, '🌒'); // The explicit type annotations here are optional,

There are a couple of ways to access the elements within a tuple. The first way merely involves fetching them in order, starting at 0:


let unsigned_32: u32 = my_tuple.0;
let floating_32: f32 = my_tuple.1;
let moon: char = my_tuple.2;

You may also use pattern matching to access these elements. The following syntax constructs a pattern out of the above:


let my_tuple: (u32, f32, char) = (5, 5.5, '🌒'); // The explicit type annotations here are optional,

let (unsigned_32, floating_32, moon) = my_tuple;

Tuples can have elements that are of varying types, as seen above. They can be useful in describing a set, fixed amount of elements, such as a set of coordinates:


let location: (i32, i32) = (10, 45);

Arrays

An array is a collection of multiple values. Unlike tuples, these values must be the same type. Arrays are like lists - and are useful for having a fixed amount of types and having data stored on the stack.

Arrays are typically used less often than vectors, which is a type of collection that includes a number of convenience methods for sorting and manipulating the values within the collection. Vectors can grow and shrink in size, as the data is managed by the heap, whereas arrays are fixed-length and stored on the stack.


let my_pets_ages: [i32; 3] = [4, 5, 3];

Notice in the type declaration, [i32; 3], that the first part denotes the type of each element within the array with each type being an i32. The second portion within the square brackets, 3, denotes the maximum amount of elements an array can hold. It's possible to do without this explicit declaration, as the compiler will interpret it as it stands.

To access specific elements within an array is done using square brackets, along with the index of the element you wish to access. As with tuples, all arrays start at index 0:


let course_modules = ["Module 1", "Module 2", "Module 3","Module 4", "Module 5"];
// Use the [ index ] syntax to access a particular element.
let module_one = course_modules[0];

Be forewarned - it's possible to access an element that doesn't exist, which would cause a runtime error.


error: this operation will panic at runtime
--> src/main.rs:32:18
|
32 | let module_six = course_modules[5];
| ^^^^^^^^^^^^^^^^^ index out of bounds: the length is 5 but the index is 5
|
= note: `#[deny(unconditional_panic)]` on by default

info

How can the Rust compiler know when a runtime error will occur? Because an array is fixed length, it knows the maximum index that can be accessed, in this case, 4. The compiler disallows it before the program is even compiled. However, if this index is a user-generated value, which can only exist at runtime, then a panic will occur and the program will stop. The fact that the program stops is a security measure as it prevents any possibly invalid memory from being accessed and exploited.

Try it yourself!

What is happening here?

We define an array a fixed-length list of values of the same type stored on the stack. We can successfully access and print the items in this list; however, the compiler prevents any out-of-bounds access. It also prevents any additional items from being appended, as the array has a fixed length in memory.

Next, we create an array of tuples, which would look like:


let tuple_array = [("hi", 3), ("hello", 1)];
for tuple in tuple_array {
println!("{:?}", tuple)
}

- + \ No newline at end of file diff --git a/docs/Rust/section2/functions-comments.html b/docs/Rust/section2/functions-comments.html index 7a1a1ea29..2e9002781 100644 --- a/docs/Rust/section2/functions-comments.html +++ b/docs/Rust/section2/functions-comments.html @@ -5,13 +5,13 @@ Functions & Comments in Rust | Polkadot Education Initiative - +

Functions & Comments in Rust

With the knowledge of variables and data types, it is time to put them to use. Functions in Rust should be familiar. The main function is one, for example, that you have already seen several times in the Rust Playground.

// This is a function!
fn main() {}

Functions contain pieces of logic in a context-specific scope. They are useful for splitting your program into more digestible pieces rather than just using a single main function to operate.

Declaring a function is simple - start with fn, followed by the name and two empty parentheses, and closed with curly brackets:

fn main() {
// The main function is no longer alone here.
do_something_interesting();
}

fn do_something_interesting() {
println!("Something interesting!");
}

You may have noticed that we called the do_something_interesting() function within our main function. It doesn't matter where this function is declared or what it does. It will execute as long as it's valid, safe Rust code.

Parameters

Functions in this form aren't useful. The above is pointless; we could accomplish the same result with less code.

Parameters allow for functions to have more dynamic and custom input. For example, let's take the previous example and add a parameter:

fn main() {
// The main function is no longer alone here.
do_something_interesting(true);
}

fn do_something_interesting(is_interesting: bool) {
println!("Is this interesting: {is_interesting}.");
}
// Output: Is this interesting: true.

A parameter is added within the previously empty parentheses. It takes the name, is_interesting followed by a colon (:), and the type, bool. This tells the function that it also expects a boolean to be included as a parameter when it is called. These parameters become part of the function's signature, or the unique layout of the function.

You can include multiple parameters of multiple types:

fn main() {
// The main function is no longer alone here.
do_something_interesting(true, "Bader");
}

fn do_something_interesting(is_interesting: bool, name: &str) {
println!("Hey, {name}! Is this interesting: {is_interesting}.");
}
// Output: Hey, Bader! Is this interesting: true.

Statements and Expressions

It's essential to differentiate statements versus expressions in Rust. Functions in Rust are statements that can end in an expression. The difference is:

  • Statements perform some modification and do not return any value.
  • Expressions provide a conclusion in the form of a value.

A simple way to think about this is when you declare something purely definitive in Rust, whether a variable or a function, it is a statement.

// This is a statement - it states that x is `10` and does not return anything.
let x = 10;

For expressions, they must evaluate and express a final value. Take this example:

// Defining a new scope within main.
let y = {
let x = 3;
x + 1 // An expression that returns 4
};

Notice the lack of a semicolon at the end of x + 1. As soon as you add a semicolon to a line's end, it becomes a statement. Expressions do not have semicolons appended to the end of them.

Return types

A key part of functions in Rust is the ability to specify a return type. With the knowledge that we can use expressions to have a conclusion to a series of operations, we can use the following syntax to add a return type and result to our function:

// This function takes a number and returns the squared version of it.
fn square(x: i32) -> i32 {
x * x
}

Notice the return type is denoted by the arrow (->) followed by the type we wish to return. To return the type, we simply return the expression without a semicolon. It's possible to also explicitly define a return statement:

// This function takes a number and returns the squared version of it.
fn square(x: i32) -> i32 {
// This is also valid!
return x * x;
}

Comments

Comments are used to document parts of your code in order to provide clarification when needed. Not every line needs to be commented on. However, it may be helpful to provide context in certain situations. Double forward slashes usually precede them (//).

Comments are purely for developers and not realized in the compiler.

You have already seen comments in action within the examples in this course. Take the previous example:

fn main() {
// The main function is no longer alone here.
do_something_interesting(true, "Bader");
}
// Hey, I'm a comment! I explain things
// This function does something interesting, apparently.
fn do_something_interesting(is_interesting: bool, name: &str) {
println!("Hey, {name}! Is this interesting: {is_interesting}.");
}
// Output: Hey, Bader! Is this interesting: true.

In the last module, you'll learn how to properly document your code using comments in a way that Cargo can understand.

Try it out!

What is happening here?

A new function with zero parameters is introduced, called do_something_better. You should be able to add a new parameter of type f64 (for double precision) like so:


fn do_something_better(number: f64) {
println("{number}");
}

To expand it, let's specify a return type - in this case, we want to return the number we passed in but squared:


fn do_something_better(number: f64) -> f64 {
number * number
}

Notice the lack of a semicolon, which denotes a resultant value in the form of an expression.

- + \ No newline at end of file diff --git a/docs/Rust/section2/heap-vs-stack.html b/docs/Rust/section2/heap-vs-stack.html index 89d819feb..03801f062 100644 --- a/docs/Rust/section2/heap-vs-stack.html +++ b/docs/Rust/section2/heap-vs-stack.html @@ -5,13 +5,13 @@ The Heap vs. The Stack | Polkadot Education Initiative - +

The Heap vs. The Stack

Throughout this course, a common occurrence presented is the notion of "putting" variables on either the stack or the heap. This page provides a summary as to what each is and how they relate to programming in Rust. Low-level languages have to be concerned with memory management and allocation. This concept should be familiar if you're coming from a language like C or C++.

What do the stack and heap represent?

The stack and heap are memory management and allocation concepts. They relate to how memory in any program is managed, accessed and whether or not it can be changed in the runtime. The stack and heap are two common methods to accomplish a program's memory management and allocation.

The Stack

The stack is a linear model of storing objects on a "stack." It is faster than the heap but cannot be modified as easily. Objects are pushed and popped to and from the stack accordingly. In Rust, items that are of known size are stored on the stack. Known size in this context refers to some value that can be calculated at compile time, for example:

let hello = "Hello, World";
// or
let x = 10;

Both hello and x are pushed onto the stack because they are known in size, meaning their memory profile can be determined at compile time because their contents are defined preemptively.

In Rust, the stack is the default for managing primitive/basic values, call frames and local variables. Non-primitives can also be put on the stack, but this is dependent on their lifetime and usage (i.e., a struct that has elements of known size). The stack itself also has a limited size, hence why the stack can overflow if too much memory is allocated to it.

The Heap

The heap refers to a dynamically managed, hierarchical model of storing objects. It is slower than the stack but provides a way to store larger amounts of data for modification at a later time in memory. Objects stored on the heap are typically not known in size at compile time, meaning they have the potential to grow and shrink as needed. Rust has many mechanisms to manage this memory safely and avoid problems such as data races or dangling pointers.

In Rust, data structures such as Vec and String utilize the heap, which can grow and shrink within the runtime.

// A vector in Rust, the Rust equivalent to an array
let vector_of_numbers = vec![0, 1, 2];
// Change this in the runtime!
modify_vec(vector_of_numbers); // [0, 1, 2, 3]

// A growable, shrinkable "String."
let hello_string = String::from("Hello, World");
hello_string.push('!'); // Adds an ! to hello_string

When to use which? How is this used in Rust?

In Rust, this memory management and allocation is handled for you. There is no special syntax to dictate this in Rust, as it is already a part of the language. As you progress, you will learn how Rust efficiently manages memory through the ownership and borrowing systems while providing low-level access.

- + \ No newline at end of file diff --git a/docs/Rust/section2/loops.html b/docs/Rust/section2/loops.html index f214e03a1..967c6ae74 100644 --- a/docs/Rust/section2/loops.html +++ b/docs/Rust/section2/loops.html @@ -5,13 +5,13 @@ Loops & Basic Logic Flows | Polkadot Education Initiative - +

Loops & Basic Logic Flows

This section will cover two foundational parts of Rust and programming in general: loops and logic flow.

Basic Logic Flows

Logic flows, sometimes called control flows, allow for conditional statements, such as if, to be possible. It allows for branching logic to be implemented for a program based on boolean values.

Using if

As said before, implementing logic based on whether something is true or not would be very useful if statements are the cornerstone of conditional logic.

let this_is_true: bool = true;

if this_is_true {
println!("Less trust, more truth!");
}

The above code showcases a very simple example. The statement this_is_true is set to a boolean value, true, which will execute the code within the brackets when evaluated by the if statement.

It's also possible to use an exclamation mark (!) to further diverge logic:

let this_is_true: bool = true;
if !this_is_true {
println!("Less trust, more truth!");
}

This no longer prints, as it looks for the opposite of true - false.

A more realistic example is checking if a number is higher or lower than expected, like seeing if a bank balance is over $100.

let bank_balance = 101;
if bank_balance >= 100 {
println!("More than 100 dollars");
}

Using else if and else

A single if statement can only be used so much. Using else if, we can create branches of logic within a program:

let bank_balance = 56;

if bank_balance >= 100 {
println!("More than, equal to, 100 dollars");
} else if bank_balance % 2 == 0 {
println!("You have an even balance!");
} else if bank_balance <= 0 {
println!("Uh oh, it appears you're in debt!");
} else {
println!("More than 0, but less than 100!");
}

Using match

You can also use a match statement to perform similar logic, which we will investigate later. A match statement is the most basic form of pattern matching:

let age = 18;

match age {
16 => println!("Old enough to drive"),
18 => println!("An adult"),
_ => println!("Irrelevant age")
}

Iterating with loops

Loops in Rust are very useful expressions, which, if you recall, produce a value as a result of some operation.

Although Rust supports five types of loops, we will go over the most common types:

  • The loop expression - an infinite loop which goes till break is called.
  • The while expression - a loop that continues until a condition is true or false.
  • The for expression - a loop that is useful for iterating over a collection of items.

Infinite loops: loop

Using the loop keyword, we can define an expression that iterates, or loops, infinitely:

fn main() {
loop {
println!("Print forever!");
}
}

This type of loop is typically used to keep trying some operation until a condition has been fulfilled.

Conditional loops: while

A while loop will automatically stop once a condition has been fulfilled. An example would be looping until a number has reached a certain limit:


fn main() {
let mut x = 0;
while (x <= 10) {
println!("x is now: {x}");
x += 1;
}
}

In this case, we define a mutable variable x. The while loop, which defines a condition while x is less than or equal to 10 (x <= 10), executes the logic until the condition for x is met.

A while loop could also iterate over a collection if needed:


fn main() {
let months = ["Jan", "Feb", "Mar", "Apr"];
let mut index = 0;
while (index <= months.len() - 1) {
println!("The month is now: {}", months[index]);
index += 1;
}
}

This is a bit clumsy, not to mention not friendly to any updates to the months array - an index could be out of bounds if we add or take away any elements from the array. Luckily, there is a better way to accomplish this.

Looping over collections: for

The for loop is primarily used for looping over a collection of items, such as an array. You may recall that we used a for loop to loop over several course modules.

Let's refactor the code from while to for:


fn main() {
let months = ["Jan", "Feb", "Mar", "Apr"];
for month in months{
println!("The month is now: {}", month);
}
}

Besides being much more concise, there is no worry about having an index out of bounds error.

Try it out!

What is happening here?

All the core loops are illustrated above. Notice the break keyword being used within loop, so the playground does not run infinitely (and so other loops can run!).

- + \ No newline at end of file diff --git a/docs/Rust/section2/module2.html b/docs/Rust/section2/module2.html index 98e810c47..fb071e06b 100644 --- a/docs/Rust/section2/module2.html +++ b/docs/Rust/section2/module2.html @@ -5,13 +5,13 @@ 2. Intro to Basic Rust | Polkadot Education Initiative - +

2. Intro to Basic Rust

Variable Scope. Memory management. How variables interact. References. Background on Programming Safety and why it is critical for Blockchain development. Substrate introduction.

Variables and Data Types

Like other programming languages, Rust has variables which allow binding names to data. The syntax for declaring a variable is

let x = 5;

The keyword let creates a new variable and the assignment operator, =, stores a value in the variable.

Rust is a statically typed language like Java, Haskell, or Typescript, unlike Python, Javascript, or Ruby. This means that every expression in a Rust program has a data type, and that type is known at the time the program is compiled. Statically typed languages prevent many common classes of programming errors such as comparing two values that can't be compared. When you create a variable as we did in the above expression, the Rust compiler infers the data type. In this case it inferred the type i32 which means a signed 32-bit integer. When integers appear in Rust code without any more type information, i32 is the default type. But there are many other numerical data types in Rust, and we can use them by adding type annotations.

let x: u32 = 5;

By adding a type annotation, :u32, we are telling the compiler that we want this variable, x, to be of type u32, an unsigned 32-bit integer. Another way to express this line of code is by using a more specific integer literal.

let x = 5u32;

Here we have assigned the value 5u32. This is the preferred way to unambiguously express the value five as the u32 type. Since we have assigned a value of type u32, the compiler is able to infer that the variable should also be of type u32.

Mutability

By default, variables are immutable in Rust, which means that the value assigned to the variable can't be changed later on in the program.

let x = 5u32;
x = 6u32;

Here we have tried to change the value of an immutable variable. The compiler will complain about this.

TODO error

Different languages have different approaches to mutability. In functional languages like Haskell or OCaml, it is common for all variables to be immutable. Other languages like Python or Java have some variable immutable and others mutable and you have to remember which is which on a per-type basis.

Rust allows both mutable and immutable variables, and requires that the programmer specify explicitly when a variable is mutable. This allows maximum flexibility without the overhead of having to remember details about each and every type. To make a variable mutable, use the mut keyword.

let mut x = 5u32;
x = 6u32;
println!("{}", x);

When writing your own programs, you should only make variables mutable when you specifically expect them to change value. This way the Rust compiler can help you detect potential errors in your program if you try to change a variable that was never expected to change.

Shadowing and Scope

On concept that is often confused with mutability is shadowing. In fact shadowing is a completely different concept, and we'll dive into it now.

let x = 5u32;
let x = 6u32;

If we compile this program, the compiler allows it without complaining. But why? x is declared as immutable, and we saw earlier that the compiler should not let us change an immutable variable. If you don't see it already, I encourage you to pause the video, and look back to see how this code differs from our previous example where the compiler did complain about changing an immutable variable.

The difference is that we have used the let keyword a second time. This declares an entirely separate variable, that just happens to have the same name as the original variable. When a second variable with the same name is created, it is said to shadow the first one.

Consider whether this program will compile successfully.

let mut x = 5u32;
let x = 6u32;
x = 7u32;

The answer is that this program does not compile. The original variable x is mutable, but that variable is shadowed by the second variable which is also named x. When we finally try to mutate x, is is the second, immutable variable that is still in scope, and thus it can't be changed.

It is uncommon to use shadowing in the trivial way I've demonstrated here and in fact it is not a good practice in real-world programs because it leads to confusing scenarios like the ones we just seen. I've done it here only because it illustrates shadowing well for learning.

But shadowing is common in real programs, just in a slightly more complex scenario. Consider this code.

let x = 5u32;

{
let x = 6u32;
println!("x is {}", x);
}

println!("x is {}", x);

Take a moment to predict the output of this program.

x is 6
x is 5

In Rust variables have lexical scope. That is to say that the variable is in scope in the entirety of the curly braces in which it is defined. In this code, we create x in the outer scope. Then we create another x in the inner scope that shadows the original. We see the shadowing from the first println. But after the inner scope ends, the second x no longer exists and the outer x is no longer shadowed. We see this from the second println.

Scoping rules are closely related to Rust's lifetime rules which we will explore in the next module about borrowing and the borrow checker.

More Simple Data Types

Rust has many simple, or "scalar", data types built into the language. There are signed and unsigned integer types of many fixed sizes, like the i32, and u32 we saw above.

SizeUnsignedSigned
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u28

There are also the isize and usize data types that are fixed width for a particular architecture. If your PC is using the AMD64 architecture, these types will be 64 bits.

There are also floating point numerical types f32 and f64. If you are familiar with floating point arithmetic from other languages, Rust's will likely work exactly how you expect. We will not dig into the floating point types in detail in this course because floating point arithmetic is non-deterministic. That means that you may not get the Exact same result for a given operation when you run it on multiple different systems (although you will certainly get a very close result). this property makes floating point math unsuitable for distributed systems such as those built with Substrate. Later in the course we will explore an alternative called fixed point and you will implement it as an assignment.

Of course Rust has the familiar bool type which can have values true, and false.

let b = true;
let mut b2 = false;
b2 = true;

Rust also has the char type for storing a single character. Character literals are denoted with single quotes.

let c: char = 'a';

Finally, Rust has the unit type, sometimes referred to as the empty tuple. This type has only a single value.

let unit: () = ();

Compound Data Types

Like many languages, Rust supports compound types that store multiple scalar values. The two that are most common are tuples and arrays.

TODO Tuples use parentheses, each value has its own type, types may be of different values. accessed with dot operator.

TODO Arrays use square brackets, have fixed length, each value is of the same type. accessed with indices of type usize.

It is also possible to make your own data types. We will cover making custom types later in the course. For example, if you need larger integers than the built-in types allow, Substrate includes U256, and U512 types for this purpose. If you want arbitrarily large integers like the ones found in Python and other programing languages, you can consider using the num-bigint crate.

Rust Operators

  • Arithmetic - addition, subtraction, multiplication, division, remainder, exponent
  • Boolean - and, or, not
  • Comparison
  • Bitwise
- + \ No newline at end of file diff --git a/docs/Rust/section2/variables-mutability.html b/docs/Rust/section2/variables-mutability.html index bce5cf166..a1e0f5f2e 100644 --- a/docs/Rust/section2/variables-mutability.html +++ b/docs/Rust/section2/variables-mutability.html @@ -5,13 +5,13 @@ Variables & Mutability | Polkadot Education Initiative - +

Variables & Mutability

If you have ever used another programming language, the concept of variables should be familiar.

// A couple Rust variables. 
// The first holds a name (a slice of text), and the other, an age (a number).
let my_name: &str = "Bader";
let age: u32 = 22;
// We'll dive more into this line later, but just know that it is a way to print information.
println!("My name is {my_name} and I am {age} years old.");

As in other languages, variables in Rust behave mostly the same with the exception of a few unique properties, the most prominent being mutability and shadowing .

info

All variables in Rust are immutable by default. Immutable variables cannot be changed unless explicitly declared as mutable. This prevents unwanted changes to values in code.

Immutability in Rust

As mentioned before, by default, all variables in Rust are immutable. This means that once a variable has been declared, the value within cannot be changed.

// The compiler won't allow this to be changed. 
// This code would throw an error!

let age: u32 = 22;
// Increment the age by one.
age += 1;

======

error[E0384]: cannot assign twice to immutable variable `age`
--> src/main.rs:26:1
|
24 | let age: u32 = 22;
| ---
| |
| first assignment to `age`
| help: consider making this binding mutable: `mut age`
25 | // Increment the age by one.
26 | age += 1;
| ^^^^^^^^ cannot assign twice to an immutable variable

This is yet another example of how the Rust compiler prevents any illegal operations, along with a descriptive way of letting the developer know. The most interesting line to note is cannot assign twice to immutable variable, as this clearly shows the immutability property of the variable.

This same error is actually incredibly useful, as in some cases, we sometimes want to keep the value of a variable the same. The Rust compiler, by default, keeps this safe by keeping it immutable. There are many practical reasons for this, i.e., multi-developer projects, where one developer can see the program's intentions by whether a variable is mutable.

A variable's value changing unintentionally can also cause trivial bugs, but luckily the Rust compiler deals with it elegantly using immutability.

Of course, variables aren't always meant to be immutable. To allow a variable to become mutable, use the mut keyword before the variable name when declaring it:

// This works now!  Notice the `mut`, short for mutable, after `let`
let mut age: u32 = 22;
// Increment the age by one.
age += 1;

Shadowing in Rust

Along with variability, Rust introduces the concept of shadowing. Take a look at this example:

let x = 10u32;
let x: &str = "Hello!";

// Prints "Hello!"
println!("{x}");

At first glance, this may seem odd - why are there two variables with the same name and different type? Shouldn't Rust's type system prevent this from occurring?

This is what's called shadowing. The Rust compiler will take the latest value assigned to that variable name, in this case from the number 10 to Hello!, and utilize that until either the scope ends or it is shadowed again.

Shadowing is not the same as declaring a variable mutable with mut, as it remains immutable after being shadowed. The let keyword must also be used to shadow a variable, take a look at the following example:


let x = 10u32;
x = "Hello!";

The above code will fail to compile, as no new assignment is being made via let. It is trying, in essence, to assign a number of type u32 to a slice of text.


Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:3:5
|
2 | let x = 10u32;
| ----- expected due to this value
3 | x = "Hello!";
| ^^^^^^^^ expected `u32`, found `&str`

Shadowing with let reassigns the variable to a new type and value, redefining it altogether.

Constants in Rust

As with traditional, immutable variables in Rust, constants are also immutable - permanently. Constants in Rust cannot be made into mutable variables with mut. Another difference is that constants only may be set to a constant expression, meaning the value is hardcoded and not calculated as the result of some function in runtime.


const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

The naming convention for constants is all uppercase, with underscores between each word. They can be used for declaring some standard, global, and constant variables within your Rust program.

Try it out!

Try out some of these concepts yourself! There are a few things that may seem unfamiliar here, which will be covered on the next page, however - have a look at the code and familiarize yourself:

  • u32 means that a variable is a number.
  • &str means that the variable is a string literal.

What is happening here?

Three primary concepts are being put to use here - namely, immutability and shadowing. Initially, we declare two variables - name and age. age is mutable, as age is expected to change, however, a name is meant to be immutable. However, age is set to a type that we can't add to easily.

This is where shadowing comes into play. Using shadowing, redefine age to be a number of type u32 instead. This allows us to add to the age easily.

- + \ No newline at end of file diff --git a/docs/Rust/section3.html b/docs/Rust/section3.html index ad8febac5..948d6722a 100644 --- a/docs/Rust/section3.html +++ b/docs/Rust/section3.html @@ -5,13 +5,13 @@ Intro to Intermediate Rust - Ownership, Borrowing, & Slices | Polkadot Education Initiative - +
- + \ No newline at end of file diff --git a/docs/Rust/section3/borrowing.html b/docs/Rust/section3/borrowing.html index 64a88a674..8f5e036f1 100644 --- a/docs/Rust/section3/borrowing.html +++ b/docs/Rust/section3/borrowing.html @@ -5,13 +5,13 @@ Borrowing & References in Rust | Polkadot Education Initiative - +

Borrowing & References in Rust

The borrowing model in Rust is quite trivial. As mentioned, ownership issues may arise when dealing with values stored on the heap (in other words, values that aren't of fixed size and are defined at compile time).

While ownership does ensure that all memory will be allocated appropriately/deallocated, it does introduce some complexity that references and borrowing solve.

Problem: Heap-stored Values

Data types that aren't fixed size and have the potential to grow in size during runtime are stored on the heap. Because the compiler can't tell what the value could be at compile time, it's managed during the runtime.

The primary issue with this is when a potential move, or change in ownership, occurs:


fn main() {
// Create a String collection from a string literal (&str)
let name = String::from("Bader");
take_my_string(name);
// Error!
println!("{name}");
}

fn take_my_string(s: String) {
// Prints a reverse string
println!("{}", s.chars().rev().collect::<String>());
}

There is a problem with this code - that's that, in order to maintain memory safety and follow the rules of ownership, the variablename was moved to the fn take_my_string function. In other words, it owns it because it is stored on the heap.

Fixed-size variables are copied - as the compiler knows exactly what to copy, whereas values on the heap are moved. Once moved, they cannot be used within that context.

Borrowing & Moving (Lack thereof)

The borrowing system in Rust solves this issue. Using references, which are denoted by &, we can avoid moving the String directly and instead just use a reference to it. This references points to where it exists in memory, in contrast to moving it all together:


fn main() {
// Create a String collection from a string literal (&str)
let name = String::from("Bader");
// We allow the function to borrow our String as a reference.
take_my_string(&name);
// No longer an error! The main function is to maintain ownership.
println!("{name}");
}

// Now, instead of taking a String - we merely take a reference to it.
fn take_my_string(s: &String) {
// Prints a reverse string
println!("{}", s.chars().rev().collect::<String>());
}

Using a reference to name, we allow take_my_string to borrow the value versus taking complete ownership. We can perform whatever we want with it, but as soon as the function is complete, name returns back to main's ownership.

Mutable & Immutable References

Mutable references are also possible - just be aware that like borrowing and references also have several rules:

  • Only one mutable reference is allowed at a time
  • Any number of immutable references is allowed
  • References must always be valid (no dangling references: variables that return a reference that ends up getting dropped at the end of some scope).

As an example, let's modify the value of a variable, then give ownership back via borrowing:


fn main() {
// Create a String collection from a string literal (&str)
let mut name = String::from("Bader");
// We allow the function to borrow our String as a reference - but mutable
// Notice the `&mut` - this is necessary for any time we reference a mutable reference
take_my_string_and_change_it(&mut name);
// No longer an error! The main function maintains ownership.
// But we also modified the value 😎
println!("{name}");
}

// Now, instead of taking a String - we merely take a reference to it.
// Again, we must use `&mut` to signal that we expect a mutable reference
fn take_my_string_and_change_it(s: &mut String) {
s.push_str(" OOO");
}

Mutable Reference Rules

The compiler looks for any instances where more than one mutable reference may exist at a time, but more than one immutable references are fine. However, once an immutable reference is defined, no mutable reference can be made:


let mut name = String::from("Bader");

// This is an immutable reference to a mutable variable
let ref_to_name = &name;
// This is *another* immutable reference to a mutable variable
let ref_to_name_2 = &name;
// And this is a mutable reference, denoted by `&mut`
let mut_ref_to_name = &mut name;

This is primarily to avoid data races - going back to Rust's memory safety; it prevents multiple pointers from attempting to modify the same value in the program.


let mut name = String::from("Bader");

// And this is a mutable reference, denoted by `&mut`
let mut_ref_to_name = &mut name;
// And this is ANOTHER mutable reference
let mut_ref_to_name_2 = &mut name;

println!("{mut_ref_to_name}, {mut_ref_to_name_2}");

This won't compile, as the compiler will recognize that we have two potentially modifiable references to the same variable.

Try it out!

What's happening here?

This example shows cases of a mutable variable being borrowed by a function, modified, then returned to the scope of main. Notice we can still use the variable, even after it has been passed to the function.

- + \ No newline at end of file diff --git a/docs/Rust/section3/module3.html b/docs/Rust/section3/module3.html index 6dea7ae07..1cde150b8 100644 --- a/docs/Rust/section3/module3.html +++ b/docs/Rust/section3/module3.html @@ -5,7 +5,7 @@ 3. Intro to Intermediate Rust | Polkadot Education Initiative - + @@ -13,14 +13,14 @@

3. Intro to Intermediate Rust

Structs. Enums. Methods. Packages and Crates. Cargo feature - this will be important for the “std” features all over Substrate. “runtime-benchmarks” and “try-runtime”.

In this module we will begin to explore Rust's type system. We will create our own types and add methods to those types. We will also take our first look at Generics and Traits in Rust. There will be much more to cover about the type system later in the course, but in this module we will learn the most fundamental and common aspects.

Rust allows users to create two different broad categories of types. First, there are Structs which are sometimes known as "product types" or "each-of types" because a struct instance requires data in each of its fields. Second, there are Enums which are sometimes known as "sum types" or "one-of types" because an enum instance is one of multiple variants.

If you come from an object oriented programming background, structs will be familiar as they are pretty similar to the data fields on a class or object. If you come from an functional programming background, both structs and enums will likely be familiar, although the enum syntax is unlike that of OCaml or Haskell.

Structs

Structs allow packaging multiple primitive data types together into a logical package. They are very similar to the tuples we saw previously, although they make the language much more programmer friendly.

When defining a struct, you provide a name for the type, which has a capital letter by convention. Then you provide a name for each field and a type for each field.

struct Fraction {
numerator: u32,
denominator: u32,
}

To create an instance of a struct, you use the name of the type and the name of each field as before. But instead of specifying a type for each field, which has already been defined, we supply a value for each field.

let one_half = Fraction {
numerator: 1,
denominator: 2,
}

Structs, like tuples, need to have a value for each and every field. It is not valid to omit one of the fields.

let one = Fraction {
numerator: 1,
}

Similarly, each field must be of the type that was declared when the struct was defined.

let one_half = Fraction {
numerator: 1u64,
denominator: 2u64,
}

Now that we know how declare and instantiate structs, let's see how to access their fields. To access the inner data of a struct, we just use a dot operator and then the name of the field.

let my_frac = Fraction {
numerator: 2,
denominator: 3,
}

let my_bottom_value = my_frac.denominator;
assert!(my_bottom_value == 3);

Notice that here we have used simple integer literals like 3 as opposed to explicitly typed ones like 3u32. This is allowed because the Rust compiler knows that the fields of the Fraction type are u32 because they were declared as such. Similarly, the compiler can infer that the type of my_bottom_value is u32 so we don't need an explicit annotation.

We can use the same syntax to mutate the value of a field. But, as always in Rust, to make a value mutable it must be explicitly declared as such.

let mut f1 = Fraction {
numerator: 1,
denominator: 2,
};

let f2 = Fraction {
numerator: 1,
denominator: 2,
};

// here we can mutate the denominator of f1
f1.denominator = 3;

// but this line will not compile because f2 is immutable
f2.denominator = 4;

Tuple Structs

In some cases you want to give your type a name like you can with structs, but you don't want to give each field a name. One such example would be storing a point on a 2D lattice.

So far we've learned two ways to store this data, both of which work, but neither of which are exactly what we want. We could simply use a tuple, but this does not allow us to name the type, and could lead to confusion and even bugs if tuples are used in another context in the program. -Or we could use a struct, but this require repeating field names often when the position is a well-established mathematical convention.

// We could simply use a tuple.
let point = (2u32, 3u32);

// We could use a struct
struct Point {
x_coord: u32,
y_coord: u32,
}

For cases like this, Rust has the tuple struct. This allows us to name the type while referencing the fields positionally.

struct Point(u32, u32);

When using tuple structs, the fields are accessed with the dot operator and an integer index exactly like they are when working with tuples.

let buried_treasure = Point(4, 5);

let treasure_x_coord = buried_treasure.0;

It is also possible to create tuple structs with a single element inside. You may wonder why you would ever want to do this when you could simply use the underlying type directly, but in fact this is done frequently, and is often referred to as the "new type pattern". The new type pattern allows us the leverage Rust's type system to distinguish values of different semantic units even if they are represented digitally by the same underlying primitive type.

A typical example would be something like storing Temperature data when you want to be sure that the units are in Celsius as opposed to something like Kelvin or Fahrenheit.

/// A temperature value that is stored in the celsius units.
struct Celsius(u32);

As a side note, notice the comment starting with a triple slash here. This is known as a doc comment or a documentation comment. Doc comments can precede data types, functions, and many other pieces of code and be used to generate automated documentation. We will not explore this in depth here, but know that it is good practice to use doc comments when creating your own types.

Finally, it is possible to create unit structs that have no fields at all. They are analogous to Rust's built-in unit type (). This is useful when you want a type to declare static methods on. We will cover methods later in this module.

/// A unit type that has no data fields.
struct MyUnitType;

Custom types in Functions and Other Types

Now that we've created some custom types of our own, let's see some ways that they can be used.

One way we can use our custom types is as parameters to functions. Let's define a function that allows us to multiply two fractions.

fn multiply_fractions(a: Fraction, b: Fraction) -> Fraction {
Fraction {
numerator: a.numerator * b.numerator,
denominator: a.denominator * b.denominator,
}
}

Now we are ready to see the value of the new type pattern we discussed previously. Consider steam engine control circuit with a function that determines whether the boiler has warmed up enough to start the engine.

/// First attempt that is subject to unit confusion
fn warm_enough(temp: u32) -> bool {
temp > 100
}

In this naive function we run the risk of a user entering a Fahrenheit temperature by mistake, or interfacing with a temperature probe that is incorrectly configured to report Fahrenheit temperature.

We can make this function safer by using Rust's type system to insist that a Celsius temperate is entered. This program will not even compile if a plain u32 is passed.

struct Celsius(u32);

fn warm_enough(temp: Celsius) -> bool {
temp.0 > 100
}

Another place we can use our custom types is as fields of other custom types. Consider a geometry program that builds up shapes from more fundamental primitive types.

struct Point(u32, u32);

/// We define a Rectangle by its two opposite corners
struct Rectangle {
top_left: Point,
bottom_right: Point,
}

Enums

TODO

Methods

We saw previously how we can write functions that take our custom data types as parameters or return our custom data types. Rust also supports the notion of a method. A method is a special function that is defined in the context of a particular type.

If you are familiar with Object Oriented languages like Java, methods will be very familiar. In Rust, methods are defined in a separate block than the type itself, unlike Java where the methods and data are all part of the same class. This adds some flexibility to Rust as we will see. Methods always take function

Methods are very similar to functions in syntax. The only difference is that they go in an impl block. As an illustrative example, lets re-write the multiply_fraction function the we looked at earlier as a method.

impl Fraction {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(self, other: Fraction) -> Fraction {
Fraction {
numerator: self.numerator * other.numerator,
denominator: self.denominator * other.denominator,
}
}
}

This method is very similar to the function we wrote previously. The main difference is the first parameter, self. Notice that this self has a lowercase s like any other variable name and no type annotation. The first parameter of a method is always an instance of the type that the method is associated with, in this case Fraction, and it is always called self. Otherwise this method is the same as our previous standalone function.

To call this method, we call it on an instance of the type using dot notation like in many other languages.

let f1 = Fraction {
numerator: 1,
denominator: 2,
};

let f2 = Fraction {
numerator: 1,
denominator: 2,
};

let f3 = f1.multiply(f2);

One weakness of the method as it is written above is that it takes ownership of both of the original fractions, and consequently, de-allocates them when it returns. A better signature would only borrow the two original fractions.

impl Fraction {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(&self, other: &Fraction) -> Fraction {
Fraction {
numerator: self.numerator * other.numerator,
denominator: self.denominator * other.denominator,
}
}
}

In the previous two versions of this function, we've explicitly listed the type with which the function is associated, namely, Fraction, several times. While this is perfectly valid Rust, it is also possible to use the type Self which has a capital S like other types in Rust. The Self is only available in the context of an impl block, and it refers to whatever type the function is associated with. Here's how the function looks when we use the Self type.

impl Fraction {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(self, other: Self) -> Self {
Self {
numerator: self.numerator * other.numerator,
denominator: self.denominator * other.denominator,
}
}
}

It is also valid, although not particularly idiomatic, to mix some uses of Self with some uses of Fraction.

API Methods

We've seen already that we can access fields of our Fraction type by using the dot operator and the name of the field. However, this only works if we are in the same module that defines the struct, or if thei fields are declared as public with the pub keyword. We will discuss visibility and API design later in this unit. But for now, know that it is often not possible to access the fields of types that are defined in foreign code.

Rather, it is often the case that a programmer-friendly API is defined on the types to prevent accidentally introducing inconsistent data. Some of the most common such methods are accessor and modifier methods aka "getters" and "setters".

Continuing the example of our Fraction type, such methods would look like this.

impl Fraction {
/// Access the numerator of a given fraction
fn get_numerator(&self) -> u32{
self.numerator
}

/// Change the numerator of a given fraction
fn set_numerator(&mut self, new_numerator: u32) {
self.numerator = new_numerator
}
}

The getter method should contain no surprises as all the relevant concepts were already introduced in our multiply fractions example. The setter however demonstrates that methods can borrow the self parameter mutably. Because the set_numerator method will mutate the fraction on which it is called, it can only be called if the fraction was defined as mutable with the mut keyword.

The setter and getter methods very common and very important, but they are also somewhat trivial. Let's consider a more interesting method, one to reduce a fraction to its simplified form. Here we will focus on the signature of the function, and leave the implementation to you, the learner, as an exercise. Notice that it is perfectly acceptable to have multiple impl blocks for the same type.

impl Fraction {
/// Reduce a Fraction in-place by mutating its numerator and denominator.
fn reduce(&mut self) {
// Implementation left as an exercise.
}
}

Like the setter, this method may mutate the fields of the fraction, and thus the signature indicates a mutable borrow.

Associated Functions

Finally we will discuss the concept of associated function. In fact, all methods are associated functions. But not all associated functions are methods. You may remember I said previously that all methods take an instance of the type called self as the first parameter. Well a function may still appear in an impl block without this self parameter, and such functions are called associated functions, but they are not called method.

CAUTION: The jargon here is different than Java. In Java, methods without the this (analogous to self) parameter are still called methods. In fact they are called static methods. Although the language is different, the concepts are still the same. And if you use the term "static method" Rust programmers are likely to know what you mean, although doing so is technically incorrect.

As an example of a static method, let's consider the API we might provide for a consumer of our Fraction library to create a new fraction.

impl Fraction {
fn new(numerator: u32, denominator: u32) -> Self {
Self{
numerator: numerator,
denominator: denominator,
}
}
}

This function is very straight forward, and simply puts the supplied data into the appropriate fields.

I'll take this opportunity to mention a short-hand syntax that is available in Rust and makes the life of the programmer a little nicer. In our new function, we had local variables called numerator and denominator and we put them into fields with exactly the same names. When assigning a field from a local variable with exactly, the same name, it is valid Rust to elide the field name entirely, and only put the value. The compiler knows what field you are using based on the variable name.

impl Fraction {
fn new(numerator: u32, denominator: u32) -> Self {
Self{
numerator,
denominator,
}
}
}

Generics

Many of the types we've defined in this module are more restrictive than they need to be. For example, our Fraction type insists that its inner values be of type u32. It may be that in some cases, programmers want more precision and prefer to use u64 instead. It would be a shame to have to re-write the entire struct and all of its methods just to change all the u32 to u64.

To address this problem, Rust's Type system has a notion of Generic types, or "generics" for short. Code that uses generics and related concepts can become quite complex, and we will dive into that full complexity in due course, but for now, let's take a look at a simple use of generic types.

/// A fraction type that allows arbitrarily large or small numerators and denominators
struct Fraction<T> {
numerator: T,
denominator: T,
}

In this improved definition of our Fraction type, we do not explicitly define the type that the numerator and denominator field will be. Rather we say they will be some generic type T. We define the single generic type in angle brackets after the name of the struct, Fraction. This means that T can be replaced by any type we may wish to use. While T can indeed be any type, because both numerator and denominator are given the type T, that means that they must be the same type. They cannot be two different types; that would require two generic parameters.

When we create instances of our new generic fraction type, we must fill in the generic type.

let my_precise_fraction = Fraction<u128> {
numerator: 236,
denominator: 473,
}

let my_low_memory_fraction = Fraction<u8> {
numerator: 1,
denominator: 4,
}

We have succeeded in creating a Fraction type that allows us to use any precision integer wa want! But we have also introduced a few problems here. For example, it is now possible to create fractions with data types that don't make any sense at all. Consider this example.

let silly_fraction = Fraction<bool> {
numerator: true,
denominator: false,
}

Not only does this bool fraction not make any sense, we also won't be able to multiply bool fractions together because booleans themselves can't be multiplied. In the next video we will see how Rust's traits solve this problem.

Traits

Traits are Rusts way of allowing programmers to define shared abstract behavior that may exist on multiple types. This concept is present in most modern programming languages. For example it manifests as interfaces in Java, type classes in Haskell, and mix-ins in Ruby.

Area Example

As a first example, let's consider a few 2D geometry types. Each of these types has different fields, but all of them are have some area, and it is valid to expect that their area can be calculated from their fields. Let's consider a Square and a Circle for example

struct Square {
side_length: u32,
}

struct Circle {
radius: u32,
}

Any geometry program is likely to want to know the area of these various shapes. We already know how to implement an area method on each of them. But implementing a one-off method on each type is problematic.

One surface-level problem is that programmers might choose slightly different names for the area method on each struct.

impl Square {
fn get_area(&self) -> u32 {
//--snip--
}
}

impl Circle {
fn calculate_area(&self) -> u32 {
//--snip--
}
}

A second, more fundamental, problem is that we may want to write a function that can work with any object whose area can be calculated, and we need a way to express to the compiler that a particular type can have its area calculated. Traits solve both of these problems.

Let's define a trait for any type that can have its area calculated.

trait Area {
fn area(&self) -> u32;
}

To do this, we use the keyword trait and then the name of the trait which starts with a capital letter, just like types do. Inside the body of the trait, we list some function signatures, but we do not include the body. Rather we just end the signature with a semicolon. The body of the function can be different for each type that implements the trait. The concept of writing the function signature without the body should be familiar to C and C++ developers as it is similar to how header files work in those languages.

There are a few items other than just function signatures that can go inside a trait definition, but we will save that discussion for later in the course.

Let's implement this trait for our two structs.

impl Area for Square {
fn area(&self) -> u32 {
self.side_length * self.side_length
}
}

impl Area for Circle {
fn area(&self) -> u32 {
const PI: u32 = 3; // LOL. This is a good approximation, right?
PI * self.radius * self.radius
}
}

We can see here that we have solved the shallower naming problem because the trait defines the name of the area function once for every type that implements it.

Trait Bounds for Function parameters

Now let's see how we can solve the deeper problem of writing a function that works with any shape that can have it's area calculated. Imagine that we want to know how many cans of paint we will need to cover a particular shape. For this we use a feature of Rust called a trait bound.

fn how_many_cans<T: Area>(shape: T) -> u32 {
/// Each can of paint covers 4 units (eg m^2)
const one_can: u32 = 4;

// Get the area of the shape in question
let area_to_paint = shape.area();

// We basically just divide. But we need to be sure there is
// extra paint, not slightly too little paint.
(area_to_paint + one_can - 1) / one_can
}

There are a few new things happening in this function signature. The first is the presence of the generic parameter T in a function. We previously saw how to declare generics on a struct, and it is quite similar for functions. Any generic types go in angle brackets immediately after the name of the function. But it is more than just a generic parameter. This time we also introduce a trait bound. That's the : Area part. This syntax says that while the how_many_cans function can be used with multiple different types, it can't be used with just any old type. It must be used with a type that implements our Area trait.

By using this trait bound, we ensure that any type passed to this function implements the necessary area trait. And if you try to pass a different type, for example bool, then your code won't compile.

Conditional Method Implementation.

It turns out that trait bounds are the exact tool we need to make our generic Fraction type work. Let's take a look at how we can use trait bounds here.

As a reminder, here's where we left our Fraction type.

struct Fraction<T> {
numerator: T,
denominator: T,
}

We discussed that leaving the type T unbounded is a problem because a user could try to use the type bool. Such a fraction does not make any sense at all, but concretely it doesn't make sense because bools, can't be multiplied. Let's see if the compiler can tell us that itself.

impl<T> Fraction<T> {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(&self, other: &Self) -> Self {
Self {
numerator: self.numerator * other.numerator,
denominator: self.denominator * other.denominator,
}
}
}

First notice the syntax for implementing a method on a struct that has generics. we start with impl<T> which can be understood as "for all types, T, implement the following. Then, as before, we name the type that we are associating this method with.

error[E0369]: cannot multiply `T` by `T`
--> src/main.rs:10:39
|
10 | numerator: self.numerator * other.numerator,
| -------------- ^ --------------- T
| |
| T
|
help: consider restricting type parameter `T`
|
5 | impl<T: std::ops::Mul<Output=T>> Fraction<T> {
| +++++++++++++++++++++++++

Indeed, the compiler tells us that we "cannot multiply T by T". It also offers a very helpful suggestion using the Mul trait from the standard library. The compiler's suggestion will work here, and I encourage you to confirm that by making the suggested change. However, the understand the Mul trait, we need to first learn about associated types, which we will not cover until later in the course. So for now, let's invent our own Multiply trait for any type that can be multiplied together, and implement it for a few primitive unsigned integer types.

trait Multiply {
fn multiply(&self, other: &Self) -> Self;
}

impl Multiply for u8 {
fn multiply(&self, other: &Self) -> Self {
self * other
}
}

impl Multiply for u16 {
fn multiply(&self, other: &Self) -> Self {
self * other
}
}

impl Multiply for u32 {
fn multiply(&self, other: &Self) -> Self {
self * other
}
}

// Could also so u64, u128, but the point is made.

We can now tell the compiler that our fraction multiplication method can be used for any fraction whose inner values implement this Multiply trait.

impl<T: Multiply> Fraction<T> {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(&self, other: &Self) -> Self {
Self {
numerator: self.numerator.multiply(&other.numerator),
denominator: self.denominator.multiply(&other.denominator),
}
}
}

Of course, we now need to call the multiply method instead of just using the * operator. But the more interesting difference is the trait bound T: Multiply. This can be understood as "For all types T that implement the Multiply trait, implement this method".

Our fraction type is now able to support multiplication as long as the inner data type also supports it, and we communicate that through a trait bound.

Trait Bounds on Type Definitions

In the previous video we showed how to use a trait bound on the impl block where we associated the multiply method with our Fraction type. In this video I want to dig on on what exactly we did there, and what we didn't do yet.

In fact, we did not prohibit creating instances of type Fraction<bool>. With the code from the previous video it is still perfectly possible to create such bool fractions. But what we did do is express to the compiler that for certain generic types, it is now possible to multiply fractions together. This is an interesting and powerful feature of Rust. We were able to conditionally associate a method with our Fraction<T> type depending on whether or not its generic types meets a particular trait bound. If the generic type implements the Multiply trait, the the instance has a multiply method. But if the generic type does not implement this trait, then the method will not be associated.

Having observed this powerful aspect of Rust, you may be thinking, "Yeah, that is useful and interesting, but in this case, do we really want to allow even creating weird fractions that can't be multiplied? Well if you don't we can express that in Rust as well. All we have to do is add... you guessed it, a trait bound... to the struct definition.

struct Fraction<T: Multiply> {
numerator: T,
denominator: T,
}

By adding the trait bound to the struct definition, we have now made it impossible to even create a Fraction whose generic type cannot be multiplied. We do still need to leave the bound on the impl block as well though.

Implementing Multiply for our Fraction type

It may have occurred to you that since our Fraction type is able to be multiplied now, perhaps we should implement our Multiply trait for it rather than just associating a one-off method. I totally agree, so let's do it. It actually only takes a small adjustment to our previous impl block.

impl<T: Multiply> Multiply for  Fraction<T> {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(&self, other: &Self) -> Self {
Self {
numerator: self.numerator.multiply(&other.numerator),
denominator: self.denominator.multiply(&other.denominator),
}
}
}

All we had to do was change the impl block to impl block. We still have the same trait bound as before, but we've added Multiply for indicating we're now implementing a trait. Since our function signature already matched the trait's expected signature, we are done. If we had previously called out one-off method something different like "times" or something, we would have to make minor adjustments there as well.

Limitation

As a final note, I'll mention one property of Rust's type system that sometimes feels like a limitation. You can only implement a trait for a type if either the trait or the type (or both) are defined in the same crate as the implementation. While this can sometimes feel like a limitation, it is actually very important to prohibit this. If we allowed implementing Foreign traits on Foreign types, then it may be that multiple conflicting implementation exist in different crates, and the compiler would be unable to catch this because it checks one crate at a time.

It is unlikely that you'll encounter this limitation until you have a little more rust experience under your belt. But when you do encounter it, know that there is a work around, and it is to use the new type pattern that we discussed previously, and then implement the trait on your new type.

Some Common Traits and Collections

In this module we have discussed how to create your own types, both structs and enums, create your own traits for abstracting shared behavior, and implement your traits on your types. In this final video, we'll take a look through some of the most common types and traits that are defined in the standard library and how you might encounter them.

TODO flesh this out.

Types

Vec +Or we could use a struct, but this require repeating field names often when the position is a well-established mathematical convention.

// We could simply use a tuple.
let point = (2u32, 3u32);

// We could use a struct
struct Point {
x_coord: u32,
y_coord: u32,
}

For cases like this, Rust has the tuple struct. This allows us to name the type while referencing the fields positionally.

struct Point(u32, u32);

When using tuple structs, the fields are accessed with the dot operator and an integer index exactly like they are when working with tuples.

let buried_treasure = Point(4, 5);

let treasure_x_coord = buried_treasure.0;

It is also possible to create tuple structs with a single element inside. You may wonder why you would ever want to do this when you could simply use the underlying type directly, but in fact this is done frequently, and is often referred to as the "new type pattern". The new type pattern allows us the leverage Rust's type system to distinguish values of different semantic units even if they are represented digitally by the same underlying primitive type.

A typical example would be something like storing Temperature data when you want to be sure that the units are in Celsius as opposed to something like Kelvin or Fahrenheit.

/// A temperature value that is stored in the celsius units.
struct Celsius(u32);

As a side note, notice the comment starting with a triple slash here. This is known as a doc comment or a documentation comment. Doc comments can precede data types, functions, and many other pieces of code and be used to generate automated documentation. We will not explore this in depth here, but know that it is good practice to use doc comments when creating your own types.

Finally, it is possible to create unit structs that have no fields at all. They are analogous to Rust's built-in unit type (). This is useful when you want a type to declare static methods on. We will cover methods later in this module.

/// A unit type that has no data fields.
struct MyUnitType;

Custom types in Functions and Other Types

Now that we've created some custom types of our own, let's see some ways that they can be used.

One way we can use our custom types is as parameters to functions. Let's define a function that allows us to multiply two fractions.

fn multiply_fractions(a: Fraction, b: Fraction) -> Fraction {
Fraction {
numerator: a.numerator * b.numerator,
denominator: a.denominator * b.denominator,
}
}

Now we are ready to see the value of the new type pattern we discussed previously. Consider steam engine control circuit with a function that determines whether the boiler has warmed up enough to start the engine.

/// First attempt that is subject to unit confusion
fn warm_enough(temp: u32) -> bool {
temp > 100
}

In this naive function we run the risk of a user entering a Fahrenheit temperature by mistake, or interfacing with a temperature probe that is incorrectly configured to report Fahrenheit temperature.

We can make this function safer by using Rust's type system to insist that a Celsius temperate is entered. This program will not even compile if a plain u32 is passed.

struct Celsius(u32);

fn warm_enough(temp: Celsius) -> bool {
temp.0 > 100
}

Another place we can use our custom types is as fields of other custom types. Consider a geometry program that builds up shapes from more fundamental primitive types.

struct Point(u32, u32);

/// We define a Rectangle by its two opposite corners
struct Rectangle {
top_left: Point,
bottom_right: Point,
}

Enums

TODO

Methods

We saw previously how we can write functions that take our custom data types as parameters or return our custom data types. Rust also supports the notion of a method. A method is a special function that is defined in the context of a particular type.

If you are familiar with Object Oriented languages like Java, methods will be very familiar. In Rust, methods are defined in a separate block than the type itself, unlike Java where the methods and data are all part of the same class. This adds some flexibility to Rust as we will see. Methods always take function

Methods are very similar to functions in syntax. The only difference is that they go in an impl block. As an illustrative example, lets re-write the multiply_fraction function the we looked at earlier as a method.

impl Fraction {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(self, other: Fraction) -> Fraction {
Fraction {
numerator: self.numerator * other.numerator,
denominator: self.denominator * other.denominator,
}
}
}

This method is very similar to the function we wrote previously. The main difference is the first parameter, self. Notice that this self has a lowercase s like any other variable name and no type annotation. The first parameter of a method is always an instance of the type that the method is associated with, in this case Fraction, and it is always called self. Otherwise this method is the same as our previous standalone function.

To call this method, we call it on an instance of the type using dot notation like in many other languages.

let f1 = Fraction {
numerator: 1,
denominator: 2,
};

let f2 = Fraction {
numerator: 1,
denominator: 2,
};

let f3 = f1.multiply(f2);

One weakness of the method as it is written above is that it takes ownership of both of the original fractions, and consequently, de-allocates them when it returns. A better signature would only borrow the two original fractions.

impl Fraction {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(&self, other: &Fraction) -> Fraction {
Fraction {
numerator: self.numerator * other.numerator,
denominator: self.denominator * other.denominator,
}
}
}

In the previous two versions of this function, we've explicitly listed the type with which the function is associated, namely, Fraction, several times. While this is perfectly valid Rust, it is also possible to use the type Self which has a capital S like other types in Rust. The Self is only available in the context of an impl block, and it refers to whatever type the function is associated with. Here's how the function looks when we use the Self type.

impl Fraction {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(self, other: Self) -> Self {
Self {
numerator: self.numerator * other.numerator,
denominator: self.denominator * other.denominator,
}
}
}

It is also valid, although not particularly idiomatic, to mix some uses of Self with some uses of Fraction.

API Methods

We've seen already that we can access fields of our Fraction type by using the dot operator and the name of the field. However, this only works if we are in the same module that defines the struct, or if their fields are declared as public with the pub keyword. We will discuss visibility and API design later in this unit. But for now, know that it is often not possible to access the fields of types that are defined in foreign code.

Rather, it is often the case that a programmer-friendly API is defined on the types to prevent accidentally introducing inconsistent data. Some of the most common such methods are accessor and modifier methods aka "getters" and "setters".

Continuing the example of our Fraction type, such methods would look like this.

impl Fraction {
/// Access the numerator of a given fraction
fn get_numerator(&self) -> u32{
self.numerator
}

/// Change the numerator of a given fraction
fn set_numerator(&mut self, new_numerator: u32) {
self.numerator = new_numerator
}
}

The getter method should contain no surprises as all the relevant concepts were already introduced in our multiply fractions example. The setter however demonstrates that methods can borrow the self parameter mutably. Because the set_numerator method will mutate the fraction on which it is called, it can only be called if the fraction was defined as mutable with the mut keyword.

The setter and getter methods very common and very important, but they are also somewhat trivial. Let's consider a more interesting method, one to reduce a fraction to its simplified form. Here we will focus on the signature of the function, and leave the implementation to you, the learner, as an exercise. Notice that it is perfectly acceptable to have multiple impl blocks for the same type.

impl Fraction {
/// Reduce a Fraction in-place by mutating its numerator and denominator.
fn reduce(&mut self) {
// Implementation left as an exercise.
}
}

Like the setter, this method may mutate the fields of the fraction, and thus the signature indicates a mutable borrow.

Associated Functions

Finally we will discuss the concept of associated function. In fact, all methods are associated functions. But not all associated functions are methods. You may remember I said previously that all methods take an instance of the type called self as the first parameter. Well a function may still appear in an impl block without this self parameter, and such functions are called associated functions, but they are not called method.

CAUTION: The jargon here is different than Java. In Java, methods without the this (analogous to self) parameter are still called methods. In fact they are called static methods. Although the language is different, the concepts are still the same. And if you use the term "static method" Rust programmers are likely to know what you mean, although doing so is technically incorrect.

As an example of a static method, let's consider the API we might provide for a consumer of our Fraction library to create a new fraction.

impl Fraction {
fn new(numerator: u32, denominator: u32) -> Self {
Self{
numerator: numerator,
denominator: denominator,
}
}
}

This function is very straight forward, and simply puts the supplied data into the appropriate fields.

I'll take this opportunity to mention a short-hand syntax that is available in Rust and makes the life of the programmer a little nicer. In our new function, we had local variables called numerator and denominator and we put them into fields with exactly the same names. When assigning a field from a local variable with exactly, the same name, it is valid Rust to elide the field name entirely, and only put the value. The compiler knows what field you are using based on the variable name.

impl Fraction {
fn new(numerator: u32, denominator: u32) -> Self {
Self{
numerator,
denominator,
}
}
}

Generics

Many of the types we've defined in this module are more restrictive than they need to be. For example, our Fraction type insists that its inner values be of type u32. It may be that in some cases, programmers want more precision and prefer to use u64 instead. It would be a shame to have to re-write the entire struct and all of its methods just to change all the u32 to u64.

To address this problem, Rust's Type system has a notion of Generic types, or "generics" for short. Code that uses generics and related concepts can become quite complex, and we will dive into that full complexity in due course, but for now, let's take a look at a simple use of generic types.

/// A fraction type that allows arbitrarily large or small numerators and denominators
struct Fraction<T> {
numerator: T,
denominator: T,
}

In this improved definition of our Fraction type, we do not explicitly define the type that the numerator and denominator field will be. Rather we say they will be some generic type T. We define the single generic type in angle brackets after the name of the struct, Fraction. This means that T can be replaced by any type we may wish to use. While T can indeed be any type, because both numerator and denominator are given the type T, that means that they must be the same type. They cannot be two different types; that would require two generic parameters.

When we create instances of our new generic fraction type, we must fill in the generic type.

let my_precise_fraction = Fraction<u128> {
numerator: 236,
denominator: 473,
}

let my_low_memory_fraction = Fraction<u8> {
numerator: 1,
denominator: 4,
}

We have succeeded in creating a Fraction type that allows us to use any precision integer wa want! But we have also introduced a few problems here. For example, it is now possible to create fractions with data types that don't make any sense at all. Consider this example.

let silly_fraction = Fraction<bool> {
numerator: true,
denominator: false,
}

Not only does this bool fraction not make any sense, we also won't be able to multiply bool fractions together because booleans themselves can't be multiplied. In the next video we will see how Rust's traits solve this problem.

Traits

Traits are Rusts way of allowing programmers to define shared abstract behavior that may exist on multiple types. This concept is present in most modern programming languages. For example it manifests as interfaces in Java, type classes in Haskell, and mix-ins in Ruby.

Area Example

As a first example, let's consider a few 2D geometry types. Each of these types has different fields, but all of them are have some area, and it is valid to expect that their area can be calculated from their fields. Let's consider a Square and a Circle for example

struct Square {
side_length: u32,
}

struct Circle {
radius: u32,
}

Any geometry program is likely to want to know the area of these various shapes. We already know how to implement an area method on each of them. But implementing a one-off method on each type is problematic.

One surface-level problem is that programmers might choose slightly different names for the area method on each struct.

impl Square {
fn get_area(&self) -> u32 {
//--snip--
}
}

impl Circle {
fn calculate_area(&self) -> u32 {
//--snip--
}
}

A second, more fundamental, problem is that we may want to write a function that can work with any object whose area can be calculated, and we need a way to express to the compiler that a particular type can have its area calculated. Traits solve both of these problems.

Let's define a trait for any type that can have its area calculated.

trait Area {
fn area(&self) -> u32;
}

To do this, we use the keyword trait and then the name of the trait which starts with a capital letter, just like types do. Inside the body of the trait, we list some function signatures, but we do not include the body. Rather we just end the signature with a semicolon. The body of the function can be different for each type that implements the trait. The concept of writing the function signature without the body should be familiar to C and C++ developers as it is similar to how header files work in those languages.

There are a few items other than just function signatures that can go inside a trait definition, but we will save that discussion for later in the course.

Let's implement this trait for our two structs.

impl Area for Square {
fn area(&self) -> u32 {
self.side_length * self.side_length
}
}

impl Area for Circle {
fn area(&self) -> u32 {
const PI: u32 = 3; // LOL. This is a good approximation, right?
PI * self.radius * self.radius
}
}

We can see here that we have solved the shallower naming problem because the trait defines the name of the area function once for every type that implements it.

Trait Bounds for Function parameters

Now let's see how we can solve the deeper problem of writing a function that works with any shape that can have it's area calculated. Imagine that we want to know how many cans of paint we will need to cover a particular shape. For this we use a feature of Rust called a trait bound.

fn how_many_cans<T: Area>(shape: T) -> u32 {
/// Each can of paint covers 4 units (eg m^2)
const one_can: u32 = 4;

// Get the area of the shape in question
let area_to_paint = shape.area();

// We basically just divide. But we need to be sure there is
// extra paint, not slightly too little paint.
(area_to_paint + one_can - 1) / one_can
}

There are a few new things happening in this function signature. The first is the presence of the generic parameter T in a function. We previously saw how to declare generics on a struct, and it is quite similar for functions. Any generic types go in angle brackets immediately after the name of the function. But it is more than just a generic parameter. This time we also introduce a trait bound. That's the : Area part. This syntax says that while the how_many_cans function can be used with multiple different types, it can't be used with just any old type. It must be used with a type that implements our Area trait.

By using this trait bound, we ensure that any type passed to this function implements the necessary area trait. And if you try to pass a different type, for example bool, then your code won't compile.

Conditional Method Implementation

It turns out that trait bounds are the exact tool we need to make our generic Fraction type work. Let's take a look at how we can use trait bounds here.

As a reminder, here's where we left our Fraction type.

struct Fraction<T> {
numerator: T,
denominator: T,
}

We discussed that leaving the type T unbounded is a problem because a user could try to use the type bool. Such a fraction does not make any sense at all, but concretely it doesn't make sense because bools, can't be multiplied. Let's see if the compiler can tell us that itself.

impl<T> Fraction<T> {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(&self, other: &Self) -> Self {
Self {
numerator: self.numerator * other.numerator,
denominator: self.denominator * other.denominator,
}
}
}

First notice the syntax for implementing a method on a struct that has generics. we start with impl<T> which can be understood as "for all types, T, implement the following. Then, as before, we name the type that we are associating this method with.

error[E0369]: cannot multiply `T` by `T`
--> src/main.rs:10:39
|
10 | numerator: self.numerator * other.numerator,
| -------------- ^ --------------- T
| |
| T
|
help: consider restricting type parameter `T`
|
5 | impl<T: std::ops::Mul<Output=T>> Fraction<T> {
| +++++++++++++++++++++++++

Indeed, the compiler tells us that we "cannot multiply T by T". It also offers a very helpful suggestion using the Mul trait from the standard library. The compiler's suggestion will work here, and I encourage you to confirm that by making the suggested change. However, the understand the Mul trait, we need to first learn about associated types, which we will not cover until later in the course. So for now, let's invent our own Multiply trait for any type that can be multiplied together, and implement it for a few primitive unsigned integer types.

trait Multiply {
fn multiply(&self, other: &Self) -> Self;
}

impl Multiply for u8 {
fn multiply(&self, other: &Self) -> Self {
self * other
}
}

impl Multiply for u16 {
fn multiply(&self, other: &Self) -> Self {
self * other
}
}

impl Multiply for u32 {
fn multiply(&self, other: &Self) -> Self {
self * other
}
}

// Could also so u64, u128, but the point is made.

We can now tell the compiler that our fraction multiplication method can be used for any fraction whose inner values implement this Multiply trait.

impl<T: Multiply> Fraction<T> {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(&self, other: &Self) -> Self {
Self {
numerator: self.numerator.multiply(&other.numerator),
denominator: self.denominator.multiply(&other.denominator),
}
}
}

Of course, we now need to call the multiply method instead of just using the * operator. But the more interesting difference is the trait bound T: Multiply. This can be understood as "For all types T that implement the Multiply trait, implement this method".

Our fraction type is now able to support multiplication as long as the inner data type also supports it, and we communicate that through a trait bound.

Trait Bounds on Type Definitions

In the previous video we showed how to use a trait bound on the impl block where we associated the multiply method with our Fraction type. In this video I want to dig on on what exactly we did there, and what we didn't do yet.

In fact, we did not prohibit creating instances of type Fraction<bool>. With the code from the previous video it is still perfectly possible to create such bool fractions. But what we did do is express to the compiler that for certain generic types, it is now possible to multiply fractions together. This is an interesting and powerful feature of Rust. We were able to conditionally associate a method with our Fraction<T> type depending on whether or not its generic types meets a particular trait bound. If the generic type implements the Multiply trait, the the instance has a multiply method. But if the generic type does not implement this trait, then the method will not be associated.

Having observed this powerful aspect of Rust, you may be thinking, "Yeah, that is useful and interesting, but in this case, do we really want to allow even creating weird fractions that can't be multiplied? Well if you don't we can express that in Rust as well. All we have to do is add... you guessed it, a trait bound... to the struct definition.

struct Fraction<T: Multiply> {
numerator: T,
denominator: T,
}

By adding the trait bound to the struct definition, we have now made it impossible to even create a Fraction whose generic type cannot be multiplied. We do still need to leave the bound on the impl block as well though.

Implementing Multiply for our Fraction type

It may have occurred to you that since our Fraction type is able to be multiplied now, perhaps we should implement our Multiply trait for it rather than just associating a one-off method. I totally agree, so let's do it. It actually only takes a small adjustment to our previous impl block.

impl<T: Multiply> Multiply for  Fraction<T> {
/// Multiply two fractions together, and return the Product
/// as a third new fraction
fn multiply(&self, other: &Self) -> Self {
Self {
numerator: self.numerator.multiply(&other.numerator),
denominator: self.denominator.multiply(&other.denominator),
}
}
}

All we had to do was change the impl block to impl block. We still have the same trait bound as before, but we've added Multiply for indicating we're now implementing a trait. Since our function signature already matched the trait's expected signature, we are done. If we had previously called out one-off method something different like "times" or something, we would have to make minor adjustments there as well.

Limitation

As a final note, I'll mention one property of Rust's type system that sometimes feels like a limitation. You can only implement a trait for a type if either the trait or the type (or both) are defined in the same crate as the implementation. While this can sometimes feel like a limitation, it is actually very important to prohibit this. If we allowed implementing Foreign traits on Foreign types, then it may be that multiple conflicting implementation exist in different crates, and the compiler would be unable to catch this because it checks one crate at a time.

It is unlikely that you'll encounter this limitation until you have a little more rust experience under your belt. But when you do encounter it, know that there is a work around, and it is to use the new type pattern that we discussed previously, and then implement the trait on your new type.

Some Common Traits and Collections

In this module we have discussed how to create your own types, both structs and enums, create your own traits for abstracting shared behavior, and implement your traits on your types. In this final video, we'll take a look through some of the most common types and traits that are defined in the standard library and how you might encounter them.

TODO flesh this out.

Types

Vec String BTreeMap BTreeSet - Note there are also hashtable based maps and sets with similar interfaces, but we will not be looking at them because they are not available in the Substrate Runtime.

Traits

Iterator Display Debug -Default

- +Default

+ \ No newline at end of file diff --git a/docs/Rust/section3/ownership.html b/docs/Rust/section3/ownership.html index d98500ecf..4d9320a52 100644 --- a/docs/Rust/section3/ownership.html +++ b/docs/Rust/section3/ownership.html @@ -5,13 +5,13 @@ Rust's Ownership Model | Polkadot Education Initiative - +

Rust's Ownership Model

As mentioned in the intro, a crucial part of Rust's safety is its memory management. Ownership in Rust is the concept that defines a set of rules for how to handle memory in a Rust program. It does not affect performance - as ownership checks happen at compile time.

While these rules may seem prohibitive, they sometimes ensure entirely memory-safe code.

note

If you aren't familiar with the stack and heap, make sure you read this first, as it will provide context for why ownership is essential when dealing with collections.

Ownership Rules

As stated in the Rust book, ownership has three primary concepts/rules:

  • Every single value (statement) has an owner
  • There can only be one owner at a time
  • Ownership is heavily dependent on the scope. When a statement goes out of scope, the ownership is dropped.

These rules ensure that the ownership of a particular variable in memory is managed correctly and appropriately. Simply put - the scope defines the ownership of a particular variable, as you will see.

Variable Scope

Let's run through an example to demonstrate the importance of scope and how it relates to ownership:

// You can define a new scope within main using this syntax.
// Everything within this scope is valid until the closing curly bracket.
// For example, you cannot use `x` outside of this scope - as the variable is dropped from memory

{
// Declare an integer x
let x = 10;
// We can do whatever we want in the context of this scope,
// but after it ends, we cannot use x.
}
// We cannot use `x` here!

In terms of ownership, the variable x is owned by this scope, defined by curly brackets, until the end of the scope. From here, the Rust compiler handles the de-allocation of memory in a safe manner.

Ownership gets more complex when dealing with values not defined at compile-time. For more information on the complexities of ownership, read the Rust book's explanation.

Try it out!

What's happening here?

We define two variables here: x and y. Both are fixed size, and known at compile time. The difference between the two is that y is owned by the scope of the main function, while x is owned by another inner scope. The code illustrates that x is dropped after the inner scope ends, while y can still be used until the end of the main function's scope.

- + \ No newline at end of file diff --git a/docs/Rust/section3/slices.html b/docs/Rust/section3/slices.html index c548f5586..fa3f734cd 100644 --- a/docs/Rust/section3/slices.html +++ b/docs/Rust/section3/slices.html @@ -5,13 +5,13 @@ Slices in Rust | Polkadot Education Initiative - +

Slices in Rust

Slices are another type of reference. As the name implies, they refer to a slice of information, in bytes, to an existing variable in memory.

Instead of handling an entire collection, which could be costly performance-wise, we can use slices to handle a reference to those items. These slices can be modified in place or returned as immutable as part of a function.

String slices

Working with string and byte slices is one of the most common tasks when working with Rust, especially at a lower level.

A String slice is simply a reference to a part, or slice, of a String:


let name = String::from("Bader");
// We can use the `..` operator to specify a range for the slice to cover
// In this case, we want the 'Bad' from Bader
let bad_slice = &name[0..3];
println!("{bad_slice}"); // Prints "Bad"

The double period syntax (..) signifies a range. It creates a variable that represents that specific String in memory from those specified positions ([starting_index..ending_index]).

Modifying a slice in-place

It's possible to modify a mutable slice. This is also called changing it 'in place', as you are manipulating the data within that specific starting and ending index.

fn main() {
let mut arr = [1, 2, 3, 4, 5];
modify_in_place(&mut arr[2..4]); // pass in a mutable slice that refers to elements 3 and 4 of the array
println!("{:?}", arr); // prints "[1, 2, 6, 8, 5]"
}

fn modify_in_place(slice: &mut [i32]) {
for i in 0..slice.len() {
slice[i] *= 2; // double each element in the slice
}

We can modify a slice, or part of the array (arr) versus the entire array. This way, we can only act on the part of the data given.

Try it yourself!

What's happening here?

The code above illustrates how to construct a basic string slice as well as return it in a function. To make it interesting, try to pass name as a mutable reference - does it work? Why and why not?

- + \ No newline at end of file diff --git a/docs/Rust/section4.html b/docs/Rust/section4.html index 146bb6ef6..a013ce66d 100644 --- a/docs/Rust/section4.html +++ b/docs/Rust/section4.html @@ -5,13 +5,13 @@ Intro to Intermediate Rust - Enums & Matching Patterns | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Rust/section4/enums.html b/docs/Rust/section4/enums.html index 305002199..1d091e978 100644 --- a/docs/Rust/section4/enums.html +++ b/docs/Rust/section4/enums.html @@ -5,13 +5,13 @@ Enums in Rust | Polkadot Education Initiative - +

Enums in Rust

Enumerations, or enums are data structures that allow for a list of pre-defined options in Rust. They are useful for pattern matching, defining expected conditions, errors, and more.

An example of enums in use would be defining different types of animal classes. An enum's values are called variants:

// Use the `enum` keyword to define an enum
// This derive statement is a macro that ensures we can use the `==` to compare enum values
// Here, we can see there are four variants that belong inside of the `AnimalType` enum.
#[derive(PartialEq)]
enum AnimalType {
Mammal,
Reptile,
Birds,
Fish
}

In a function, we can, for example, take any type that is AnimalType, which in turn means there are four possible values as to what it can be. The program can choose to implement different behavior based on the value of the enum:

note

This is where a match statement from the previous module would be ideal to use, as it will allow us to specify a case for each possible value in the AnimalType enum.


fn take_animal(name: &str, animal_type: AnimalType) {
if animal_type == AnimalType::Mammal {
println!("{name} is a mammal!");
}
}

This function takes a string literal as a name, &str, and specifically looks for whether the passed in AnimalType is a mammal or not. Enum values are accessed using a double colon, ::, and can be checked as a conditional statement if needed.

However, this function is rather bland and doesn't account for the other possibilities of AnimalType.

Basic Pattern Matching with Enums

If you recall, we used a match statement in the last module to cover a case where a variable could either be true or false. Applying this same concept with enums, we can ensure that all possible cases are covered.

This concept of matching a case to an outcome is called pattern matching, and is a very common design pattern in Rust.

// Same function, but expanded to cover all cases with `match`
fn take_animal(name: &str, animal_type: AnimalType) {
// The match state
match animal_type {
// The animal is a mammal!
AnimalType::Mammal => println!("{name} is a mammal!"),
// The animal is a reptile!
AnimalType::Reptile => println!("{name} is a reptile!"),
// The animal is a bird!
AnimalType::Birds => println!("{name} is a bird!"),
// The animal is a fish!
AnimalType::Fish => println!("{name} is a fish!"),
};
}

While this is entirely possible with a series of if and else if statements, using match is a more concise and powerful way to pair a specific input to a particular output. The value of animal_type, the match statement will print a line appropriate to that specific input.

info

Anything that is 'conditional', i.e., true or false, can be pattern matched. In future modules, you will see more examples of advanced pattern matching that take advantage of this powerful feature.

The syntax for specifying a match pattern starts with the keyword match, followed by the name of the value you wish to pattern match. Each possibility is then paired with a specific output using an arrow (=>):

// The possibility => output
AnimalType::Mammal => println!("{name} is a mammal!"),

Try it yourself!

What's happening here?

This code defines an enum, AnimalType, with four variants. A function is defined, take_animal, that calls for a string literal and AnimalType as parameters. A match statement then matches the input of AnimalType to the desired outcome.

- + \ No newline at end of file diff --git a/docs/Rust/section4/error-handling.html b/docs/Rust/section4/error-handling.html index 74ea0a1ae..2526f7296 100644 --- a/docs/Rust/section4/error-handling.html +++ b/docs/Rust/section4/error-handling.html @@ -5,13 +5,13 @@ Error handling with Result & Option in Rust | Polkadot Education Initiative - +
-

Error handling with Result & Option in Rust

As seen before, panicking when the program is running is unideal and should be avoided if necessary. Rust includes two commonly used enums that help ensure data is valid in runtime: Result and Option.

Both types ensure invalid data and errors are handled adequately and do not cause the program to panic.

Using Option

Option is an enum that contains two variants - None and Some:

enum Option<T> {
None,
Some(T),
}

The T here may be a new sight to behold. This is a generic type parameter, which will be covered in module six. For now, know it means that any type can be within Some - it doesn't matter what it is, just that something is there.

A concrete example would be attempting to access an empty array. Instead of panicking and attempting to access an index that doesn't exist, we can return an Option

// An example of a function that returns an Option, aka, Some(i32) or None.
// Notice the angled brackets which contain the type we're expecting.
fn safe_access(index: usize, slice: &[i32]) -> Option<i32> {
// We check to see if the length of the slice is zero, or
// less than the requested index. If it is, we return `None`
if slice.len() == 0 || slice.len() < index {
return None;
}
// Otherwise, we're good to return the requested item!
Some(slice[index])
}

None does the opposite. If the Option is None, it implies the data does not exist. This is useful for checking the state of some data but does not describe any error associated with a potential failure.

let empty_array = [];
let valid_array = [1, 2, 3];
// Pass it in as a reference, as per the function signature

safe_access(1, &empty_array); // returns None
safe_access(1, &valid_array); // returns Some(2)

Pattern Matching with Option

Because Option returns an enum, it would be good practice to ensure that we handle both variants. Functions can be called and matched directly:

let empty_array = [];
let valid_array = [1, 2, 3];

// Technically could be None or Some
// hint: look at the type of this variable
let maybe_value: Option<i32> = safe_access(1, &valid_array);

// However, let's match the function directly:
match safe_access(1, &valid_array) {
Some(value) => println!("We have a value: {value}"),
None => println!("It doesn't exist :()")
};

Using if let with Option

Alternatively, an if let statement may be used instead of match. if let essentially will peform the same type of pattern matching, where it will look for Some value, assign it to a variable if it exists, and safely unwrap it:

let valid_array = [1, 2, 3];

if let Some(value) = safe_access(0, &valid_array) {
println!("{value}"); // 1
} else {
println!("Nothing valid was found!");
}

Using Result

A Result also contains two variants, Ok(T) and Err(E). While the generic T still implies any value, the generic E can be used to define a custom error type, allowing us to describe why a particular value or scenario did not output as expected.

It is used very similarly to Option at times, with the exception that instead of returning None, it returns an Error that describes what went wrong. Usually, Option should be used when a value is not certain to be existent or not, and Result used when referring to a computation that has the possibility of failing.

You may read more on Option vs. Result here.

enum Result<T, E> {
Ok(T),
Err(E),
}

Here is the example above, only adapted to use a Result:

fn safe_access_result(index: usize, slice: &[i32]) -> Result<i32, String> {
if slice.len() == 0 || slice.len() < index {
return Err(String::from("Error! Invalid index!"));
}
Ok(slice[index])
}

A key aspect is the E, or Err, part of this expected return type. It's a String, meaning while it enables us to express why it has failed, it is unideal.

Defining a Custom Error Type For Result

Rather than having obscure Strings as an Error type, we can instead define a custom enum to represent any potential errors:

enum SafeArrayError {
InvalidIndex
}

With this type defined, we can now change our function to the following:

enum SafeArrayError {
InvalidIndex
}

fn safe_access_result(index: usize, slice: &[i32]) -> Result<i32, SafeArrayError> {
if slice.len() == 0 || slice.len() < index {
// Now, we return a context-specific error.
return Err(SafeArrayError::InvalidIndex);
}
Ok(slice[index])
}

Try it yourself!

What's happening here?

There are two functions, safe_access and safe_access_result. These functions both accomplish the same task - ensure a particular element of an array can be accessed safely. A custom error type is also defined, SafeArrayError, which is utilized within the safe_access_result function.

- +

Error handling with Result & Option in Rust

As seen before, panicking when the program is running is unideal and should be avoided if necessary. Rust includes two commonly used enums that help ensure data is valid in runtime: Result and Option.

Both types ensure invalid data and errors are handled adequately and do not cause the program to panic.

Using Option

Option is an enum that contains two variants - None and Some:

enum Option<T> {
None,
Some(T),
}

The T here may be a new sight to behold. This is a generic type parameter, which will be covered in module six. For now, know it means that any type can be within Some - it doesn't matter what it is, just that something is there.

A concrete example would be attempting to access an empty array. Instead of panicking and attempting to access an index that doesn't exist, we can return an Option

// An example of a function that returns an Option, aka, Some(i32) or None.
// Notice the angled brackets which contain the type we're expecting.
fn safe_access(index: usize, slice: &[i32]) -> Option<i32> {
// We check to see if the length of the slice is zero, or
// less than the requested index. If it is, we return `None`
if slice.len() == 0 || slice.len() < index {
return None;
}
// Otherwise, we're good to return the requested item!
Some(slice[index])
}

None does the opposite. If the Option is None, it implies the data does not exist. This is useful for checking the state of some data but does not describe any error associated with a potential failure.

let empty_array = [];
let valid_array = [1, 2, 3];
// Pass it in as a reference, as per the function signature

safe_access(1, &empty_array); // returns None
safe_access(1, &valid_array); // returns Some(2)

Pattern Matching with Option

Because Option returns an enum, it would be good practice to ensure that we handle both variants. Functions can be called and matched directly:

let empty_array = [];
let valid_array = [1, 2, 3];

// Technically could be None or Some
// hint: look at the type of this variable
let maybe_value: Option<i32> = safe_access(1, &valid_array);

// However, let's match the function directly:
match safe_access(1, &valid_array) {
Some(value) => println!("We have a value: {value}"),
None => println!("It doesn't exist :()")
};

Using if let with Option

Alternatively, an if let statement may be used instead of match. if let essentially will perform the same type of pattern matching, where it will look for Some value, assign it to a variable if it exists, and safely unwrap it:

let valid_array = [1, 2, 3];

if let Some(value) = safe_access(0, &valid_array) {
println!("{value}"); // 1
} else {
println!("Nothing valid was found!");
}

Using Result

A Result also contains two variants, Ok(T) and Err(E). While the generic T still implies any value, the generic E can be used to define a custom error type, allowing us to describe why a particular value or scenario did not output as expected.

It is used very similarly to Option at times, with the exception that instead of returning None, it returns an Error that describes what went wrong. Usually, Option should be used when a value is not certain to be existent or not, and Result used when referring to a computation that has the possibility of failing.

You may read more on Option vs. Result here.

enum Result<T, E> {
Ok(T),
Err(E),
}

Here is the example above, only adapted to use a Result:

fn safe_access_result(index: usize, slice: &[i32]) -> Result<i32, String> {
if slice.len() == 0 || slice.len() < index {
return Err(String::from("Error! Invalid index!"));
}
Ok(slice[index])
}

A key aspect is the E, or Err, part of this expected return type. It's a String, meaning while it enables us to express why it has failed, it is unideal.

Defining a Custom Error Type For Result

Rather than having obscure Strings as an Error type, we can instead define a custom enum to represent any potential errors:

enum SafeArrayError {
InvalidIndex
}

With this type defined, we can now change our function to the following:

enum SafeArrayError {
InvalidIndex
}

fn safe_access_result(index: usize, slice: &[i32]) -> Result<i32, SafeArrayError> {
if slice.len() == 0 || slice.len() < index {
// Now, we return a context-specific error.
return Err(SafeArrayError::InvalidIndex);
}
Ok(slice[index])
}

Try it yourself

What's happening here?

There are two functions, safe_access and safe_access_result. These functions both accomplish the same task - ensure a particular element of an array can be accessed safely. A custom error type is also defined, SafeArrayError, which is utilized within the safe_access_result function.

+ \ No newline at end of file diff --git a/docs/Rust/section4/panic.html b/docs/Rust/section4/panic.html index c0634a6fc..7a31b41f3 100644 --- a/docs/Rust/section4/panic.html +++ b/docs/Rust/section4/panic.html @@ -5,13 +5,13 @@ Panic! in Rust | Polkadot Education Initiative - +

Panic! in Rust

Previously we mentioned the concept of panicking, or the program stopping during runtime. This is usually to prevent something unsafe from occurring, such as the possibility of invalid data being accessed in memory. A panic is a irrecoverable error.

However, it is possible to invoke a panic using the panic! macro:

fn main() {
println!("This program will panic!");
panic!();
}

The output of this program indicates that an explicit panic did indeed occur within the main function:

  Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.55s
Running `target/debug/playground`
thread 'main' panicked at 'explicit panic', src/main.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

There are a few methods that could cause a panic, namely within using Result. Methods such as unwrap() and expect() can cause a panic if the Result is None, as the error is not being handled. As a result, the program simply panics and closes to prevent any further unwanted behavior.

The program should only panic if it could be in a "bad" state, where there is invalid data flowing within the program. Ideally, errors should be recoverable if possible. Concepts like logic flows and pattern matching help with handling different types and Errors, which will be apparent in the next section.

- + \ No newline at end of file diff --git a/docs/Rust/section5.html b/docs/Rust/section5.html index 8ec47030d..d1f966734 100644 --- a/docs/Rust/section5.html +++ b/docs/Rust/section5.html @@ -5,13 +5,13 @@ Intro to Intermediate Rust - Data Structs & Collections | Polkadot Education Initiative - +
- + \ No newline at end of file diff --git a/docs/Rust/section5/collections.html b/docs/Rust/section5/collections.html index d6ab282bd..34a5c2d9d 100644 --- a/docs/Rust/section5/collections.html +++ b/docs/Rust/section5/collections.html @@ -5,13 +5,13 @@ Collections in Rust - Vectors, Strings, Hashmaps | Polkadot Education Initiative - +

Collections in Rust - Vectors, Strings, Hashmaps

We've already dealt with several collections. Rust's standard library provides these data structures and aid in dynamic data manipulation and representation. Similar to arrays and tuples, collections can store more than value. In contrast to such compound types, collections point to items stored on the heap. The benefit of it being managed on the heap allows collections to be growable and generally more modifiable in runtime. One collection you have already encountered numerous times throughout this course is String, which represents a list of characters.

There are three commonly used collections in Rust:

  • Vectors - re-sizable arrays. Like slices, their size is not known at compile time, but they can grow or shrink at any time.
  • Hashmaps - key-value data structures that store a map of information.
  • Strings - a collection of characters that are stored on the heap.

Vectors

The official type declaration of a vector is Vec<T>, which represents a list of any generic type T. We'll go over generics in the next module, but for now, know it is used exactly how T is used in the Option<T> and Result<T, E> enums. Like arrays, Vectors can only store values of the same type.

To create a new, empty vector, we call the associated method new():

let vector: Vec<i32> = Vec::new();
// Optionally, one may also use the `vec!` macro with initialized values. Type is inferred.
let vector = vec![1, 2, 3];

Now that the vector is defined, it exposes several methods which we can use to manipulate it:

// Initialize a new vector
let vector: Vec<i32> = Vec::new();

// Push new items to it
vector.push(2); // [2]
vector.push(3); // [2, 3]
vector.push(4); // [2, 3, 4]

// Reading via .get()
let four: Option<i32> = vector.get(2) // retrieves an Option, which can be pattern matched

// Reading a vector via indexing - same as an array
// use with caution, the index may not exist!
let four: i32 = &vector[2];

A vector follows all rules of the borrow checker - meaning if the vector is mutable, then an immutable reference to an element is impossible. Vectors also aren't limited to scalar types - you may define a struct, and store a list of structs within a vector if desired.

Strings

The String collection should be very familiar by now. String is a collection of characters stored on the heap. The &str type, or string slice/literal is different from this type, as it's a primitive type, whereas String is a data structure part of the Rust standard library. At its core, a String is a wrapped Vec of bytes.

Strings are rather complicated yet advantageous data structures. If you want to learn more about Strings at a more technical level, please read the Rust book, as this is just meant to introduce how to use String.

To create a new String, use the associated function new(), along with others:

// An empty string
let s = String::new();

// Push a string literal to a String
s.push_str("Web");

// Push a single char using .push()
s.push('3');

Hashmaps

Hashmaps store a mapping of K to V, and in other languages, are often to referred to as a map, dictionary, or hash table. As with T in Vec<T>, K and V are generic types used to refer to any type. As with vectors, values within the hashmap are stored on the heap. If an external variable is placed as part of a hashmap insertion, then the hashmap owns that variable.

To create a new hashmap, the associated method new() is also used:

let mut balances = Hashmap::new();

To add new values to the balances map. This particular mapping maps a String to an i32:

balances.insert(String::from("Bader"), 100);

To access a value, a .get(key: K) method is also exposed, upon which the value may be fetched by a reference to the key (so as to prevent ownership):

let mut balances = Hashmap::new();
balances.insert(String::from("Bader"), 100);
let key = String::from("Bader");
let bader_bal: Option<i32> = balances.get(&key);

The Option returned must be adequately handled via pattern matching, or a default value must be provided to bader_bal.

Try it yourself!

What's happening here?

All three collections and their common, respective operations are shown above. For more info, be sure to checkout the Rust Book as well as the Rust Standard Library's documentation for each of these data structures.

- + \ No newline at end of file diff --git a/docs/Rust/section5/struct-methods.html b/docs/Rust/section5/struct-methods.html index e1dfdd028..5fe1ed2a0 100644 --- a/docs/Rust/section5/struct-methods.html +++ b/docs/Rust/section5/struct-methods.html @@ -5,13 +5,13 @@ Defining Methods for Structs | Polkadot Education Initiative - +

Defining Methods for Structs

Methods are very similar to functions - the difference here being that methods are applied to structs. They breathe life into structs by providing associated logic that often utilizes its inner fields and represents an instance of that struct.

The syntax is nearly identical to a function, with the exception that its part of the implementation of the struct:

struct Person {
age: i32,
name: String,
}

// This is an implementation of 'Person'
// We can add methods on each instance of the struct
impl Person {
fn print_person(&self) {
println!("This person's name is {} and is {} years old.", self.name, self.age);
}
}
let a_person = Person {
age: 22,
name: String::from("Bader"),
};

a_person.print_person();
// This person's name is Bader and is 22 years old

In the above implementation, we defined a method, print_person, which takes &self as a parameter. &self is an immutable reference to that specific instance, in this case, a_person. To access these methods, we must first have an instance of the struct defined, then use the dot operator (.) to access the method. The &self parameter is actually a shorthand way of saying:

self: &Self -> person: &Person

Self, with a capital S, is an alias for the struct's type, whereas self with a lowercase s, refers to the actual instance with its initialized fields.

info

We borrowed &self - can you think of why?

It's also possible to accept more parameters of the same type as part of the method, for example:

impl Person {
fn print_person(&self) {
println!("This person's name is {} and is {} years old.", self.name, self.age);
}

fn is_older(&self, other: &Person) -> bool {
// Return if the other person is older or not as an expression
other.age > self.age
}
}
let a_person = Person {
age: 22,
name: String::from("Bader"),
};

let another_person_who_is_older = Person {
age: 35,
name: String::from("Johnny"),
};

a_person.is_older(&another_person_who_is_older); // false

Associated Functions

All functions under the impl (implementation) block are considered "associated" because they effectively become part of the type. Until now, you've seen all methods take self as a parameter - but it is possible to have a function that doesn't require an existing instance of the struct. They are often used as constructors to create new instances of that struct, as seen below:

impl Person {
// Notice the return type is `Self`:
fn new(age: i32, name: String) -> Self {
// Shorthand syntax for struct fields from the previous lesson!
Person {
age,
name
}
}
}

// use the :: keyword/operator to access this method
let person = Person::new(22, String::from("Bader"));

Try it yourself!

What's happening here?

This example showcases how a Person can be printed, compared against other borrowed Person(s), and how an associated function can be used to create a new Person.

- + \ No newline at end of file diff --git a/docs/Rust/section5/structs.html b/docs/Rust/section5/structs.html index 545baa6ee..98dbb141c 100644 --- a/docs/Rust/section5/structs.html +++ b/docs/Rust/section5/structs.html @@ -5,13 +5,13 @@ Data Structs in rust | Polkadot Education Initiative - +

Data Structs in rust

A struct, sometimes called a data structure, is a collection of multiple types into a single data type. It's similar to tuples in concept but provides several ways to expand and define custom struct behavior. Structs, like any other type, abide by Rust's strict typing system and allow for a plethora of expansion when it comes to programming in Rust.

Creating Structs

The struct keyword is used to instantiate struct, followed by the name, then a series of fields between curly brackets. Each field is akin to a key-value pair and must specify a type:

struct Person {
age: i32,
name: String,
}

In this example, we declared a struct called Person with two fields - a name and an age. To use this struct, you must create an instance of it by setting it equal to some variable - pay attention to its syntax:

let a_person = Person {
age: 22,
name: String::from("Bader"),
};

Notice that each field must abide by the type set when the struct was defined initially. Structs are instantiated by declaring the name (Person) followed by curly brackets, with the key-value representations of the fields specified (age: 22).

Accessing Fields

Once we've created an instance of a struct, we can use the dot operator (.) to access the values within:

let a_person = Person {
age: 22,
name: String::from("Bader"),
};

println!("The age of the person is: {a_person.age}"); // 22

Like any other variable in Rust, this one can also be declared mutable, making it possible to change the value of the struct's inner fields if desired.

Shorthand Syntax

It's rare to construct structs using this syntax. Oftentimes, a function that acts as a "blueprint" is ideal:

// A function that asks for two parameters: an age, and name.
// It returns a `Person` from this information.
fn create_person(age: u32, name: String) -> Person {
// Notice we can use the expression as the last expression
// to return a new instance of `Person`
Person {
age: age,
name: name
}
}

// Now we can use the function instead!
// It is of type 'Person', which is inferred without being explicitly defined.
let a_person: Person = create_person(22, String::from("Bader"));

It's also possible to have a "shorthand" way of representing struct fields if both the parameters and field names are the same, rather than using the key: value format:

// A function that asks for two parameters: an age and name.
// It returns a `Person` from this information.
fn create_person(age: u32, name: String) -> Person {
// Because both the parameters and struct fields are the same name,
// it's possible to write them shorthanded.
Person {
age,
name
}
}

Different types of Structs

Structs can take a couple of different forms syntactically. All are valid structs and can utilize traits and methods, just with different syntax.

Updatable Structs

If you've ever used Javascript, then you may be familiar with the spread operator: ... In Rust, you can use this to create a new struct from an old one with values changed as needed:

// Instead of doing this:
let a_person = Person {
age: 22,
name: String::from("Bader"),
};

let older_person_same_name = Person {
age: 23,
name: a_person.name,
};

// You can use `..` to make it more concise:
let a_person = Person {
age: 22,
name: String::from("Bader"),
};

let older_person_same_name = Person {
age: 23,
// This operator now allowed for this instance of `Person`
// to inherit all attributes except age because we specified it.
..a_person
};

The .. syntax will assume all other fields as the previous struct, except for the ones which you have specified explicitly.

Tuple & Unit Structs

Structs may also be expressed in a tuple-like format:

// A struct that represents RGB color values.
struct RGB(u32, u32, u32);
let black = RGB(0, 0, 0);

The difference between this and a tuple is that this is still a struct with a unique type - meaning it's possible to implement any traits or functionality.

The same logic applies to the "unit-type" struct, which is just a struct that contains no fields at all. These are mostly used as a sort of marker to dictate what types the program should expect (and that doesn't require any sort of data):

// A struct that has no fields
struct AlwaysEqual;

Ownerships in Structs

If you observed, we used String, an ownable data type, versus &str in our struct. This was for a reason - as we want the fields within to be owned for as long as the struct lives. It is possible for structs to store references to data owned elsewhere, but only with the use of lifetimes - which will be discussed later.

Try it yourself!

What's happening here?

A struct is defined with two fields, age and name. Several instances are constructed, as well as an updated version which uses some of the previous values to create a new struct. Several other concepts are also illustrated, such as the use of the "tuple" and "unit" like structs.

- + \ No newline at end of file diff --git a/docs/Rust/section5/vectors-vs-strings.html b/docs/Rust/section5/vectors-vs-strings.html index f4b7d7261..25285cbb9 100644 --- a/docs/Rust/section5/vectors-vs-strings.html +++ b/docs/Rust/section5/vectors-vs-strings.html @@ -5,13 +5,13 @@ Vectors, Strings, and &str Slices | Polkadot Education Initiative - +

Vectors, Strings, and &str Slices

It can be rather confusing for a beginner to wrap their head around how these various types relate and how they work together.

To summarize:

  • A String is a data structure that is essentially a wrapped, managed Vec<T>. It represents a list of UTF-8 bytes that are stored on the heap. It is useful for defining typical words and sentences in a mutable fashion and can be modified during the runtime. Recall a String is just a struct, meaning it owns the fields within. In this case, it owns the bytes that you write to it.
  • String literals/slices, or &str, are immutable, predefined slices of UTF-8 bytes that are defined at compile-time, not runtime. They are either stored as 'static, aka part of the executable in static storage, or on the stack. It is also fixed-length.
  • A Vec<T> is a data structure that is more generic in nature, as it allows for any data type to be passed in to form a managed list.

When to use a string slice versus a String?

Remember, slices are "view-only", immutable references. String should be used when you require a growable, ownable collection of bytes. String is more costly - within most programs, it is not an issue, but in some environments, it may be better to utilize string slices whenever possible.

String slices can also be converted to a String rather easily:


let literal_to_string: String = "Hello".to_string();

- + \ No newline at end of file diff --git a/docs/Rust/section6.html b/docs/Rust/section6.html index eda9eab2b..b4e87c145 100644 --- a/docs/Rust/section6.html +++ b/docs/Rust/section6.html @@ -5,13 +5,13 @@ Intro to Advanced Rust - Traits, Generics, & Lifetimes | Polkadot Education Initiative - +
- + \ No newline at end of file diff --git a/docs/Rust/section6/associated-generics.html b/docs/Rust/section6/associated-generics.html index f69952d4f..a21a12c78 100644 --- a/docs/Rust/section6/associated-generics.html +++ b/docs/Rust/section6/associated-generics.html @@ -5,13 +5,13 @@ Associated Types vs Generic Types | Polkadot Education Initiative - +

Associated Types vs Generic Types

Associated types are an abstract way to define a name for what a type should be.

In the Generics section, we encountered an associated type when we defined the Mul trait as a bound:

fn square<T: Mul<Output = T> + Copy>(x: T)...

A closer look reveals an additional parameter in our trait bound declaration - Output. Output is an associated type, sometimes called an associated item, and is another feature of Rust.

Associated Types in Use

Associated types are specified as part of a trait. They are defined by moving type declarations within the trait as an Output type.

A prime example is Mul specifying any Output. Contrary to generics, this is not required to be enforced to a specific type by the compiler.

pub trait Mul<RHS = Self> {
type Output;
fn mul(self, rhs: RHS) -> Self::Output;
}

Associated Types vs Generics

While associated types and generics are used similarly and generate scalable code, there are some key differences between the two:

  • Associated types generally provide more flexibility than generics, but only within their specific trait's scope.

  • Associated types specify a trait's expected output's type, whereas generics are more the input types for a trait. Associated types represent the result of the trait's behavior on a given type.

- + \ No newline at end of file diff --git a/docs/Rust/section6/generics.html b/docs/Rust/section6/generics.html index bbf472856..b40a89062 100644 --- a/docs/Rust/section6/generics.html +++ b/docs/Rust/section6/generics.html @@ -5,13 +5,13 @@ Generic Types in Rust | Polkadot Education Initiative - +

Generic Types in Rust

Generics are a powerful concept allowing types to be "expected" or known without knowing what they are. Previously, you've encountered generics when dealing with the Option<T> enum, where T is a generic that can accept any type. They are placeholder types that optionally can use traits to define a set of expectations while still being abstract and generic.

Generics can be used in traits, methods, functions, enums, and structs.

Scenario: Why Generic Code is Useful

Let's assume that I wish to have a simple function that is meant to square two numbers, as we've previously done:

fn square(x: i32) -> i32 {
x * x
}

The above code is acceptable - however, there is a problem. What if other number types must be compared? After all, this same function could apply to f32, f64, u32, and so on.

Generics remove this redundancy by defining an abstract type, usually referred to as T, which allows this function to be compatible with many types.

Defining Generics: Functions

To convert the above function into a generic one, the following syntax must be applied:

fn square<T>(x: T) -> T {
x * x
}

The most glaring difference is the introduction of <T>. This syntax precedes the parameters list, defining what the generic is called and what properties it should entail regarding traits (more on that next!).

The type T in this context quite literally means any type, meaning the function can now be called as follows (note the use of :: to define the type):

let squared = square::<i32>(10); // 5

When we call this function, we replace the <T> with the type we wish to represent - in this case, i32. Taking a closer look at the function's signature, it essentially converts from T to i32 across the board:

fn square<i32>(square: i32) -> i32;

Now, this is possible for any number! With one caveat - not all types can utilize the * operator. For example, if you used a String, this function would panic, as it is impossible to compare Strings directly this way. The above code shouldn't work together, as the compiler is unsure what to expect since it can expect any type.

Adding Trait Bounds to Generics

Traits will be covered more in-depth in the next section. However, know that they can define certain behavior for generics to ensure the type is compatible with the function. This particular trait, Mul, is a trait that ensures that a type can be multiplied. By using the syntax T: Mul, we limit all possible types T could be to anything that implements Mul:

Output is an associated type, which will also be covered later.

fn square<T: Mul<Output = T> + Copy>(x: T) -> T {
x * x
}

This translates into our code being compatible with a whole host of various types that already implement this trait, Mul, by default.

// 32-Bit Signed Integer, note how you can also use ::<type> to define what type to expect.
let squared_i32 = square::<i32>(10);
// 32-Bit Unsigned Integer
let squared_u32: u32 = square(10);
// Floating Point Number
let squared_f32: f32 = square(10.0);

Defining Generics: Structs & Methods

As with functions, generics may be applied to structs, methods, and enums.

For structs, generics may be used to define abstract field types:

struct Point<T> {
x: T,
y: T
}

Multiple generics may be used, meaning the type of x must differ from the type of y. Generic labels usually follow this convention, but in theory, can be named anything:

struct Point<T, U> {
x: T,
y: U
}

Point can now be created with different types for x and y fields.

If we were to define some methods for Point, generics may also be used to further provide type dynamism:

impl<T, U> Point<T, U> {
fn x(&self) -> &T {
&self.x
}

fn y(&self) -> &U {
&self.y
}
}

Try it yourself!

What is happening here?

This example features two primary usages of Rust generics. The first illustrates the usage of generics within a function, which reduces the boilerplate for supporting multiple compatible types that want to utilize fn square. Generics may also be used as a part of a struct, as seen with Point. An associated type is also "hidden" in the first example, where the associated type Output is defined as part of the Mul trait when declaring the trait bound.

- + \ No newline at end of file diff --git a/docs/Rust/section6/lifetimes.html b/docs/Rust/section6/lifetimes.html index a7dbb1ed1..caa3abcab 100644 --- a/docs/Rust/section6/lifetimes.html +++ b/docs/Rust/section6/lifetimes.html @@ -5,13 +5,13 @@ Lifetimes in Rust | Polkadot Education Initiative - +

Lifetimes in Rust

If you recall, in the Ownership section of this course, a value in Rust is only as valid as its scope. Once it is out of scope, it is out of memory and disregarded. This concept is called a lifetime. Every reference in Rust has a lifetime, although they are mostly inferred.

When to Declare Explicit Lifetimes

Lifetimes are also generic, and are used to validate references. In essence, the lifetime is defined by the scope in which a particular reference is deemed valid. In other words, it uses the borrow checker to ensure that dangling pointers don't occur.

Borrows are as valid as the source:

fn main() { // Lifetime "A", or 'a.
let y;
{ // Lifetime "B", or 'b.
let x = 10;
y = &x;
}
// y here would be "dangling", as x is no longer "living".
println!("{y}");
}

The compiler tells the entire story of why this cannot work:

Compiling playground v0.0.1 (/playground)
error[E0597]: `x` does not live long enough
--> src/main.rs:5:13
|
5 | y = &x;
| ^^ borrowed value does not live long enough
6 | }
| - `x` dropped here while still borrowed
7 | // y here would be "dangling", as x is no longer "living".
8 | println!("{y}");
| - borrow later used here

Notice here that we have not explicitly defined lifetimes - as they are implicitly done for us.

Lifetime Annotations

Lifetime annotations, as mentioned, are generics. They follow the convention of an apostrophe, ', followed by the letter of going from a onwards.

Annotating Functions

To annotate a function with an explicit lifetime, use 'a like any other generic. 'a essentially means as long as the function is still alive:

fn bad_lifetime<'a>() -> &'a i32 {
let _x: i32 = 19;
// ERROR: `_x` does not live long enough; it gets dropped at the end of the function!
let y: &'a i32 = &_x;
y
}

However, as the function name suggests - this does not work, as _x does not live long enough and is deallocated from memory. y then points to that deallocated memory, which is a prime example of a dangling pointer. To fix this, we must ensure that _x is a reference that has the same lifetime as y as well as the function itself.

fn fixed_lifetime<'a>() -> &'a i32 {
let _x: &'a i32 = &19;
_x
}

Static Lifetimes

A unique lifetime is called 'static, which explicitly defines a reference as something that can live for the entirety of the program. A prime example of a 'static lifetime is &'static str, or string literals, as they are stored in the program's binary, making them always available.

Try it yourself!

What's happening here?

Lifetimes prevent dangling pointers via the Rust borrow checker. In the above example, a fixed lifetime is defined. This lifetime is valid because the lifetime, labeled 'a, on _x matches that of the function signature <'a>.

- + \ No newline at end of file diff --git a/docs/Rust/section6/traits.html b/docs/Rust/section6/traits.html index 0ab4a97ad..59dbf9929 100644 --- a/docs/Rust/section6/traits.html +++ b/docs/Rust/section6/traits.html @@ -5,13 +5,13 @@ Defining behavior with Traits | Polkadot Education Initiative - +

Defining behavior with Traits

There is no genuine concept of object-oriented-style inheritance in Rust. Traits introduce the notion of defining shared behavior for data structures. A trait defines a set of shared functions, expectations, and behavior that can be used for an indefinite number of types.

Creating a Trait

Defining a trait utilizes the trait keyword, followed by the name of the trait. In this example, notice the use of the pub (public) keyword. This exposes the trait to any external files within a Rust crate and project to utilize:

pub trait Transferrable {
fn transfer(&mut self, who: &mut Self, amount: i32) -> i32;
}

This function defines a trait Transferrable, which also defines a method signature, called transfer. This signature is a blueprint for what we expect from any type that implements Transferrable as a trait. As implied by the name, this trait would allow a type to send currency from the caller to who. The use of this trait would allow for a particular type to now possess these methods in order to achieve this functionality.

In order these methods to be used, however, they must be implemented on that specific type.

info

Take note of the difference of usage between self and Self. Remember, self refers to an already instantiated instance of that type, whereas Self refers to simply that type. The use of Self here allows for this trait to look for the specific type being implemented when it comes to who should receive currency.

Common Traits in Rust

As you may have already seen, there are quite a few traits that are used in Rust. A few common traits are:

  • Debug - formats the output in a debugging context.
  • PartialEq - A trait for equality comparisons.
  • Clone - Describes how a new value can be created, or "Cloned".

There are many more, but these, when used in conjunction with the derive macro with structs, can be very useful in making structs more pleasant to handle.

Using the derive macro

A line you may have encountered above a struct declaration is #[derive()]. This is what is called a procedural macros, which create a sort of auto-implementation for a set of compatible traits. Between the parentheses, any traits compatible with this macro, along with the struct's fields within can be implemented:

#[derive(PartialEq)]
struct Stormtrooper {
name: String
}

Because String also implements PartialEq, this is perfectly acceptable and easier than fully writing out the implementation for this particular trait. It's a shorthand way of implementing common traits within the standard library. Without PartialEq in this context, we wouldn't be able to utilize the equality operator (==).

let stormtrooper_one = StormTrooper { "Stormtrooper 1".to_string() };
let stormtrooper_two = StormTrooper { "Stormtrooper 2".to_string() };

// Possible due to PartialEq! No need for a full trait implementation.
println!("Is Stormtrooper One equal to Stormtrooper Two: {}", stormtrooper_one == stormtrooper_two);

Implementing a Trait on an "Account" struct

To implement (impl) this trait, we will create a struct called Account, which will also be marked as pub:

note

Here, the concept of an Account is more in the context of a blockchain, where an account has some identification (id, in this case, although this could also be an address) and a monetary balance. This theme will become more present throughout the course, as these terms will become more prevalent when learning more about developing with Substrate.

pub struct Account {
pub id: i32,
pub balance: i32,
pub is_legit: bool,
}

impl Transferrable for Account {}

If the above code was run, we'd be presented with this error:

error[E0046]: not all trait items implemented, missing: `transfer`
--> src/main.rs:11:1
|
8 | fn transfer(&mut self, who: &mut Self, amount: i32) -> i32;
| ------------------------------------------- `transfer` from trait
...
11 | impl Transferrable for Account {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `transfer` in implementation

For more information about this error, try `rustc --explain E0046`.

As the compiler clearly says - we must implement all method signatures for this implementation!

Now, we may implement our Transferrable trait to properly reflect the method signatures:

impl Transferrable for Account {
fn transfer(&mut self, who: &mut Self, amount: i32) -> i32 {
// Take from our (self) balance,
self.balance = self.balance - amount;
// Add that amount to someone else's balance,
who.balance = who.balance + amount;
// return our remaining balance.
self.balance
}
}

Because we implemented Transferrable with this type, now any instance of the account struct can utilize the transfer() method and ensure shared behavior:

    let mut alice = Account {
id: 1,
balance: 100,
is_legit: true,
};

let mut bob = Account {
id: 2,
balance: 200,
is_legit: true,
};

// In theory, account two could also easily call this!
alice.transfer(&mut bob, 50);

println!(
"Account One Balance: {}, Account Two Balance: {}",
alice.balance, bob.balance
);

In fact, any type that impls Transferrable can now transfer funds, so long as it's implemented correctly as per the method and trait signatures.

To illustrate this let us add a method signature to Transferrable to drain all funds from an Account:

pub trait Transferrable {
fn transfer(&mut self, who: &mut Self, amount: i32) -> i32;
/// New method signature! Now we *have* to implement it.
fn drain_funds(&mut self) -> bool;
}

impl Transferrable for Account {
fn transfer(&mut self, who: &mut Self, amount: i32) -> i32 {
// Take from our (self) balance,
self.balance = self.balance - amount;
// Add that amount to someone else's balance,
who.balance = who.balance + amount;
// return our remaining balance.
self.balance
}

fn drain_funds(&mut self) -> bool {
// Drain it all!
self.balance = 0;
// Really, we should have a Result that ensures that the account was successfully drained.
true
}
}
note

While drain_funds returns a bool for simplicity, a Result<T, E> would be more appropriate here, along with more robust checking on if the user is allowed to drain their funds or not.

And as before, it's as simple as calling the drain_funds on any type, in this case, Account, that implements the Transferrabletrait:

// Account two's balance is now 0
bob.drain_funds();

Feel free to define more types and implement, or even expand Transferrable with more methods as you see fit.

Trait Parameters & Bounds

A common use of traits is their use as bounds in functions, which can then be used to define parameters that abide by these bounds. With generics, this becomes possible and produces very reusable code:

info

For this scenario, we added an additional method to the Transferrable trait - is_legit(), which verifies the legitimacy of a particular entity. This entity could be an Account, or even something like a SmartContract type.

fn verify_entity(entity: &impl Transferrable) {
if entity.is_legit() {
println!("Entity is legit!");
} else {
println!("Entity is NOT legit!");
}
}

The function's parameter, entity, is saying something very particular here: any type which implements Transferrable as a trait is a valid parameter. If we were to define another type, say SmartContract, that implemented Transferrable, then that would also be completely valid.

It can also be written using trait bound syntax, which allows for a better view of generics at work:

fn verify_entity<T: Transferrable>(entity: &T) {
if entity.is_legit() {
println!("Entity is legit!");
} else {
println!("Entity is NOT legit!");
}
}

To call a function like this would be quite interesting, as we would need to specify which type, so long as it abides by the trait bound Transferrable:

let mut account = Account {
id: 1,
balance: 100,
};
// Account goes in the angled brackets, as it's a type 'T' that implements 'Transferrable':
verify_entity::<Account>(&account);

Try it yourself!

What's happening here?

In this example, we define a common trait Transferrable, which allows a struct to define and access three methods: transfer, is_legit, and drain_funds. When a struct implements this trait, it must specify the functionality for each method. A function, verify_entity, also introduces a trait bound T: Transferrable. This bound ensures that only types that have correctly implemented Transferrable are to be valid arguments.

- + \ No newline at end of file diff --git a/docs/Rust/section7.html b/docs/Rust/section7.html index 7243ff3ae..21fc6038f 100644 --- a/docs/Rust/section7.html +++ b/docs/Rust/section7.html @@ -5,13 +5,13 @@ Intro to Advanced Rust - Iterators & Closures | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Rust/section7/closures.html b/docs/Rust/section7/closures.html index 10faa2997..19bcce2d3 100644 --- a/docs/Rust/section7/closures.html +++ b/docs/Rust/section7/closures.html @@ -5,13 +5,13 @@ Closure Functions in Rust | Polkadot Education Initiative - +

Closure Functions in Rust

Closures are one of Rust's functional programming-esque features that can create anonymous functions. Closures, like functions, execute blocks of logic within. They are fundamentally different in how they operate and handle ownership.

They may even be used as a return type, as shown in examples such as unwrap_or_else():

let some_result: i32 = dangerous_value().unwrap_or_else(|| 42);

The two vertical pipes within unwrap_or_else signify that it is a closure, and 42 is the returned value. Closures can be used in generics using the Fn trait or even as a parameter for an actual function.

Defining & Using Closures

Defining closures is similar to defining a variable. It utilizes two vertical pipes to signify any parameters. A return type can also be specified as part of the closure. Closures are called the same way a function is called:

fn main() {
let name = "Bob";
// Closure that captures `name` and returns a new string with "Hello, " prepended
let greet_closure = |name: &str| -> String {
format!("Hello, {}!", name)
};
// It is called the same way a function is
let greeting = greet_closure(name);
println!("{}", greeting); // Prints "Hello, Bob!"
}

Type Inference

Where functions require explicit type declarations for their parameter and return types, closures don't have this requirement. They are able to infer both types, depending on how one uses it:

note

This also can shorten the closure into one line, as the curly brackets are omitted, and the return type is directly specified. If this was a multi-line closure, then curly brackets would be required.

let name = "Bob";
let greet_closure = |name| format!("Hello, {}!", name);
// It is called the same way a function is. The types are inferred here!
// `name` is of type &str, meaning it now expects it from thereon.
let greeting = greet_closure(name);
println!("{}", greeting); // Prints "Hello, Bob!"
// This will fail!
let greeting_two = greet_closure(123);
18 | let greeting = greet_closure(123);
| ------------- ^^^ expected `&str`, found integer
| |
| arguments to this function are incorrect

Capturing Environments

Unlike functions, closures can capture their environment. A closure can utilize local variables within a scope that the closure is defined:

fn main() {
let name = "Bob";
// This closure has no arguments now but can use the `name`
// variable defined in the main scope.
let greet_closure_that_captures = || format!("Hello Captured, {}!", name);
// It is called the same way a function is
let greeting_two = greet_closure_that_captures();
println!("{}", greeting); /
}

Try it yourself!

What's going on here?

The code above showcases two examples of closures in use. The first example accepts a parameter, of which its type is inferred. The second example removes the parameter and instead captures a variable outside of the closure but within the same scope.

- + \ No newline at end of file diff --git a/docs/Rust/section7/iterators.html b/docs/Rust/section7/iterators.html index bd48f1e7b..3503f15f3 100644 --- a/docs/Rust/section7/iterators.html +++ b/docs/Rust/section7/iterators.html @@ -5,13 +5,13 @@ Iterators in Rust | Polkadot Education Initiative - +

Iterators in Rust

Iterators in Rust allow you to perform tasks sequentially on a series of items while knowing the end of the sequence. Iterator is a trait that, once implemented, handles many of the nuances of safely looping through collections. Iterators are lazy, meaning they are only used when called upon.

Creating an Iterator

Most collections we discussed earlier have the Iterator trait already implemented. The most common usage of iterators regards arrays or vectors, and they are often used as an alternative for loops:

let my_vec = vec![1, 2, 3, 4, 5];

// Call the `.iter()` method on `Vec`:
let iter = my_vec.iter();
for val in iter {
println!("Value: {}", val);
}
// Optionally, call it in-line:
let my_vec = vec![1, 2, 3, 4, 5];
for val in my_vec.iter() {
println!("Value: {}", val);
}

You may notice that this looks familiar to a regular for loop. This will be discussed in more depth in a later section, but know they are different!

A closer look at the Iterator trait

Let's examine the Iterator trait itself:

pub trait Iterator {
type Item;

fn next(&mut self) -> Option<Self::Item>;
// methods with default implementations elided
}

The next() method is the heart of an Iterator's functionality, as it's the only method needed to implement the Iterator trait. next() returns the next item as Some(value) from the iterator, and returns None once it's complete.

Notice that although Iterator can iterate over many types, it uses an associated type, Item. Using an associated type allows the type implementing the trait to specify the desired outcome, versus a generic would have the developer impose some type that may be incorrect.

"Consuming" An Iterator & Iterator Methods

As a result of implementing Iterator, one can use a few key methods to modify a sequence of items. For more information on available methods, it is highly encouraged to read through the Rust documentation for the Iterator trait.

Some methods consume an iterator, meaning they call the next( ) method within their implementation. An example is .sum(), which collectively adds all items by calling next() until the iterator has reached its end:

let my_vec = vec![1, 2, 3, 4, 5];
let sum = my_vec.iter().sum();

The .map() method is very commonly used. It takes a closure, performs an operation over each item in the iterator, and returns an iterator. This does not modify the previous iterator, rather it returns a new one with modified values:

let my_vec = vec![1, 2, 3, 4, 5];
// Returns a new iterator that adds `1` to each item
my_vec.iter().map(|x| x + 1);

Another method that operates similarly is filter() , which returns an iterator of filtered values based on a conditional statement within the closure:

let my_vec = vec![1, 2, 3, 4, 5];
// Returns a new iterator that adds `1` to each item
my_vec.iter().filter(|x| *x % 2 == 0);

Using .collect()

Using collect(), you can gather the values of an iterator and back into a vector that represents the operation you performed on it:

let my_vec = vec![1, 2, 3, 4, 5];
// Returns a new iterator containing only even numbers
let filter_iter = my_vec.iter().filter(|x| *x % 2 == 0);
// Returns a new iterator that adds `1` to each item
let map_iter = my_vec.iter().map(|x| x + 1);

// Collect into a filtered Vec
let even: Vec<_> = filter_iter.collect(); // [2, 4]
// Collect into a mapped Vec
let plus_one: Vec<_> = map_iter.collect(); // [2, 3, 4, 5, 6]

As some of these methods return an iterator, it's possible to chain them:

// Both maps then filter on the returned iterator from `map`
let chained_iter = my_vec.iter().map(|x| x + 1).filter(|x| *x % 2 == 0);

Loops vs Iterators - when to use which?

In the previous section, a piece of code looked just like a for loop that was covered in the Loops module:

    let my_vec = vec![1, 2, 3, 4, 5];
for val in my_vec.iter() {
println!("Value: {}", val);
}

At first glance, this appears to be the same syntax and functionality as a normal for loop:

    let my_vec = vec![1, 2, 3, 4, 5];
for val in my_vec {
println!("Value: {}", val);
}

Both serve the same purpose but behave differently. Iterators operate on references, whereas a for loop directly takes possession of the value for the loop's scope. Calling:

for val in my_vec.iter() {}

It is effectively the same as calling:

for val in &my_vec {}

It is generally a better design decision to utilize a reference-based approach, as my_vec would be unusable after a traditional for loop that takes ownership.

Try it yourself!

What's going on here?

The code features a collection of various iterators stemming from the same Vec, my_vec. Iterators do not directly modify the Vec. Rather each operation returns a new iterator with values that may be modified, which is the case with map().

- + \ No newline at end of file diff --git a/docs/Rust/section7/macros.html b/docs/Rust/section7/macros.html index 996ca1f3d..0330a16db 100644 --- a/docs/Rust/section7/macros.html +++ b/docs/Rust/section7/macros.html @@ -5,13 +5,13 @@ Macros in Rust | Polkadot Education Initiative - +
-

Macros in Rust

Macros in Rust is, in the most basic sense, "code that writes code", also known as metaprogramming. By now, you have seen the println!() macro many times, and it illustrates how useful macros may be in everyday coding.

Another prime example that you have seen is the use of the #[derive] macro, which can implement traits on types automatically:

// Automatically implements this type.
#[derive(PartialEq)]
struct SomeType;

Macros are called before the compiler interprets the code, so they can perform these operations (i.e., implementing a trait for you). This is usually called "expanding," as the macro's code expands to actual, usable Rust code the compiler can interpret and use.

Macro Types

There are two primary types of macros:

  • Declarative or "macro_rules!" Macros
  • Procedural Macros - which also have subtypes

Declarative Macros

Declarative macros are the most widely used and often easier to write than procedural ones. They allow programmers to write expressions akin to match statements that "fill in the blank" to make writing Rust more concise. Put simply; declarative macros operate almost like a template where the parameters provided by the programmer fill in the blanks.

Procedural Macros

Procedural macros are more complex, accepting arbitrary code as input and producing code as output. This code, called a TokenStream, represents this input and output. Procedural macros operate more like a function, accepting a TokenStream as a parameter and specifying a return type as a TokenStream. Part of the complexity in creating these macros is that they must be in a separate crate, impacting the Rust project's structure.

There are three primary types of procedural macros:

  • #[derive] macros specify code to add to entities such as structs and enums.
  • Function-like macros which structurally look and work like functions.
  • Attribute-like macros which define custom attributes on a particular entity.

In this course, we won't be reviewing how to write a procedural macro. For more reading, it is encouraged to read the Rust Book's examples, as well as the The Little Book of Rust Macros for more in-depth reading on how macros can be utilized.

Writing a Basic Declarative Macro

Overview

In this example, we will be writing a declarative macro that utilizes macro_rules!. As stated before, a declarative macro similarly works in principle to a match statement, as it declares a set of rules executed in order until the condition is reached. Once the rule is met, the macro generates the corresponding Rust code.

Courtesy of The Little Book of Rust Macros, the following examples help to solidify this concept.

// Each rule looks like the following: 
($matcher) => {$expansion}

And in practice:

// This simply returns the expression: "4", aka the result of "1 + 3"
macro_rules! four {
() => { 1 + 3 };
}

fn main() {
let f = four!(); // 4
println!("{f}"); // 4
}

Creating a square! and factor! macro

Macros can also utilize metavariables to capture input and values from outside of the macro. One more commonly used is the expr metavariable, which signifies some expression as an input.

Using these concepts, let's create a macro that takes a number and squares it:

macro_rules! square {
($e:expr) => { $e * $e };
}

fn main() {
let f = square!(10); // 100
println!("{f}"); // 100
}

Slightly more advanced, let's allow our macro to take a number, find all factors, then return a new Vec of those factors:

macro_rules! square {
($e:expr) => {
$e * $e
};
}

macro_rules! find_factors {
($e:expr) => {{
let mut factors = Vec::new();
for multipler in 1..=$e {
if $e % multipler == 0 {
factors.push(multipler);
}
}
factors
}};
}

fn main() {
let f = square!(10);
let factors = find_factors!(24);
println!("{f}");
println!("{:?}", factors);
}

Try it yourself!

What is happening here?

Two declarative macros are defined, square! and find_factors!. Both take an expression and return a mutated version of the input. square! simply returns a square version of the number, while find_factors! does a few novel tasks:

  • Takes an expression, $e.
  • Defines a new inner scope to return.
  • Within that scope, creates a Vec of factors to return.
  • Declares a for loop, which iterates from the range of 1 to the value of $e (i.e.,. 1 to 24).
  • Finds if it is a factor via modulo and appends it to the array if it is.
- +

Macros in Rust

Macros in Rust is, in the most basic sense, "code that writes code", also known as metaprogramming. By now, you have seen the println!() macro many times, and it illustrates how useful macros may be in everyday coding.

Another prime example that you have seen is the use of the #[derive] macro, which can implement traits on types automatically:

// Automatically implements this type.
#[derive(PartialEq)]
struct SomeType;

Macros are called before the compiler interprets the code, so they can perform these operations (i.e., implementing a trait for you). This is usually called "expanding," as the macro's code expands to actual, usable Rust code the compiler can interpret and use.

Macro Types

There are two primary types of macros:

  • Declarative or "macro_rules!" Macros
  • Procedural Macros - which also have subtypes

Declarative Macros

Declarative macros are the most widely used and often easier to write than procedural ones. They allow programmers to write expressions akin to match statements that "fill in the blank" to make writing Rust more concise. Put simply; declarative macros operate almost like a template where the parameters provided by the programmer fill in the blanks.

Procedural Macros

Procedural macros are more complex, accepting arbitrary code as input and producing code as output. This code, called a TokenStream, represents this input and output. Procedural macros operate more like a function, accepting a TokenStream as a parameter and specifying a return type as a TokenStream. Part of the complexity in creating these macros is that they must be in a separate crate, impacting the Rust project's structure.

There are three primary types of procedural macros:

  • #[derive] macros specify code to add to entities such as structs and enums.
  • Function-like macros which structurally look and work like functions.
  • Attribute-like macros which define custom attributes on a particular entity.

In this course, we won't be reviewing how to write a procedural macro. For more reading, it is encouraged to read the Rust Book's examples, as well as the The Little Book of Rust Macros for more in-depth reading on how macros can be utilized.

Writing a Basic Declarative Macro

Overview

In this example, we will be writing a declarative macro that utilizes macro_rules!. As stated before, a declarative macro similarly works in principle to a match statement, as it declares a set of rules executed in order until the condition is reached. Once the rule is met, the macro generates the corresponding Rust code.

Courtesy of The Little Book of Rust Macros, the following examples help to solidify this concept.

// Each rule looks like the following: 
($matcher) => {$expansion}

And in practice:

// This simply returns the expression: "4", aka the result of "1 + 3"
macro_rules! four {
() => { 1 + 3 };
}

fn main() {
let f = four!(); // 4
println!("{f}"); // 4
}

Creating a square! and factor! macro

Macros can also utilize metavariables to capture input and values from outside of the macro. One more commonly used is the expr metavariable, which signifies some expression as an input.

Using these concepts, let's create a macro that takes a number and squares it:

macro_rules! square {
($e:expr) => { $e * $e };
}

fn main() {
let f = square!(10); // 100
println!("{f}"); // 100
}

Slightly more advanced, let's allow our macro to take a number, find all factors, then return a new Vec of those factors:

macro_rules! square {
($e:expr) => {
$e * $e
};
}

macro_rules! find_factors {
($e:expr) => {{
let mut factors = Vec::new();
for multiplier in 1..=$e {
if $e % multiplier == 0 {
factors.push(multiplier);
}
}
factors
}};
}

fn main() {
let f = square!(10);
let factors = find_factors!(24);
println!("{f}");
println!("{:?}", factors);
}

Try it yourself

What is happening here?

Two declarative macros are defined, square! and find_factors!. Both take an expression and return a mutated version of the input. square! simply returns a square version of the number, while find_factors! does a few novel tasks:

  • Takes an expression, $e.
  • Defines a new inner scope to return.
  • Within that scope, creates a Vec of factors to return.
  • Declares a for loop, which iterates from the range of 1 to the value of $e (i.e.,. 1 to 24).
  • Finds if it is a factor via modulo and appends it to the array if it is.
+ \ No newline at end of file diff --git a/docs/Rust/section8.html b/docs/Rust/section8.html index da3d88ec1..701d3afba 100644 --- a/docs/Rust/section8.html +++ b/docs/Rust/section8.html @@ -5,13 +5,13 @@ Learning Cargo, Rust’s Package Management System & Unit Testing | Polkadot Education Initiative - +
- + \ No newline at end of file diff --git a/docs/Rust/section8/defining-cargo-config.html b/docs/Rust/section8/defining-cargo-config.html index 81507c720..6ff4b84c8 100644 --- a/docs/Rust/section8/defining-cargo-config.html +++ b/docs/Rust/section8/defining-cargo-config.html @@ -5,13 +5,13 @@ Reading & Defining `cargo.toml` | Polkadot Education Initiative - +

Reading & Defining `cargo.toml`

If Rust is installed on your system, then cargo by default is installed. cargo is Rust's package management system and can be called from the command line.

Creating a new project with Cargo

Create a new project using cargo using the cargo new command. You will need to have your command line/terminal open. For the sake of this course, let's call the project first-cargo, which will create a new directory with your Rust project within:

cargo new first-cargo
cd first-cargo/

Upon running tree or ls -R, the file structure is revealed to be the following:

.
├── Cargo.toml
└── src
└── main.rs

2 directories, 2 files

  • src/ is where your project lives and contains the source of your Rust project. main.rs is where you can and will write executable code.

  • Cargo.toml is how your Rust project manages project metadata, dependencies, and build options.

Understanding Cargo.toml

Cargo.toml is written in the TOML format, and specifies metadata for your Rust project. Upon inspecting our project, first-cargo's Cargo.toml it should look relatively barebones:

[package]
name = "first-cargo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

Each section is defined by square brackets ([]) followed by the name within (i.e., [package]). Keys are determined by the name, followed by an equals sign (=) and the value as text in double quotes (i.e., name = "first-cargo").

  • [package] - Specifies the name of the package, version of the binary, and the Rust edition used.

  • [dependencies] - Specifies a list of local or remote external dependencies.

Building & Running with Cargo

Running cargo build within any directory with Cargo.toml will result in an attempt to build the project. This merely builds the project and generates a target/ folder with the compiled binary.

Compiling first-cargo v0.1.0 (/rust-course/first-cargo)
Finished dev [unoptimized + debuginfo] target(s) in 1.09s

Running cargo run will both build the project, as well as run the binary.

Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/first-cargo`
Hello, world!
- + \ No newline at end of file diff --git a/docs/Rust/section8/defining-crate-features.html b/docs/Rust/section8/defining-crate-features.html index a6de7dbb4..728288e21 100644 --- a/docs/Rust/section8/defining-crate-features.html +++ b/docs/Rust/section8/defining-crate-features.html @@ -5,13 +5,13 @@ Features in Cargo | Polkadot Education Initiative - +

Features in Cargo

In the last section, we defined the hex crate as an external dependency that we could use within our project with a simple name and version of the crate. However, there are other important factors to consider than declaring a version.

In some cases, such as the case in Substrate runtime development, the std library may not be available. This is due to the constraints of running in a Wasm runtime versus a native binary.

There are also other cases where std may not be available. For this reason, some crates offer the option of a no-std implementation which does not use the Rust Standard Library and can run in these resource-constrained environments.

Specifying features

A feature in the context of Rust crates refers to the ability to conditionally/optionally include (or not include) certain sub-dependencies.

To have the hex crate be compatible in a no-std environment, default-features must be declared:

[dependencies]
hex = { version = "0.4", default-features = false }

This is now more akin to a JSON object, with curly brackets encasing the keys and values within hex. Setting the default features of a crate to false will force the crate not to use std, as that was the default.

Keep in mind that not all crates support no-std environments.

- + \ No newline at end of file diff --git a/docs/Rust/section8/installing-crate.html b/docs/Rust/section8/installing-crate.html index 97f82474d..3a1a23835 100644 --- a/docs/Rust/section8/installing-crate.html +++ b/docs/Rust/section8/installing-crate.html @@ -5,13 +5,13 @@ Installing a cargo crate | Polkadot Education Initiative - +

Installing a cargo crate

In the previous section of this module, you learned how to create a new cargo project, build, and run it. This section will focus on the [dependencies] section of your Cargo.toml and how to bring in external crates for usage within your project. Crates are other Rust packages with publicly exposed types and functionality for usage in Cargo projects.

Adding a new crate

Two primary methods of declaring an external crate in Rust are directly putting it in Cargo.toml or using the cargo CLI.

Using Cargo.toml

We'll be importing the crate called hex. It is a crate that easily encodes and decodes data into hexadecimal representation.

Add this dependency as a key and value under [dependencies], like so:

[dependencies]
hex = "0.4"

The convention is the crate's name, hex, followed by the version of the crate, 0.4. To ensure everything is correctly in place without building the whole project, you may run cargo check:

Checking hex v0.4.3
Checking first-cargo v0.1.0 (/rust-course/first-cargo)
Finished dev [unoptimized + debuginfo] target(s) in 0.70s

Using the cargo CLI

Optionally, you may also use the CLI to automate this by using cargo add hex:

Updating crates.io index
Adding hex v0.4.3 to dependencies.
Features:
+ alloc
+ std
- serde

This will have the same effect as directly putting it within the TOML file.

For more information regarding Cargo and dependencies, visit the official Cargo documentation.

- + \ No newline at end of file diff --git a/docs/Rust/section8/unit-tests.html b/docs/Rust/section8/unit-tests.html index a38c38557..a1bca2136 100644 --- a/docs/Rust/section8/unit-tests.html +++ b/docs/Rust/section8/unit-tests.html @@ -5,13 +5,13 @@ Basic Unit Testing in Rust | Polkadot Education Initiative - +

Basic Unit Testing in Rust

With a basic understanding of how dependencies work in Rust, you may learn how to write and structure a basic testing structure for your Cargo project.

Unit tests allow you to test your code for bugs using a set of predefined functions.

Be sure to open your project, first-cargo, as that is the repo we will be testing.

Putting tests below the main function

Tests are defined using the #[test] macro and are usually either in their separate directory on the same level as src or, in some cases, included in the project file.

Defining tests involves defining a separate module using the mod keyword and a special macro to indicate testing is taking place below the main() function:

#[cfg(test)]
mod tests {
// Testing functions go here...
}

Once this is defined, you may create functions representing unit tests within:

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}

Looking further at this function, fn it_works(), notice the #[test] macro, which denotes that this function is a unit test.

Additional Testing Macros

assert_eq!(), assert!(), assert_ne!() and other macros are used within tests to make an assertion about a particular value.

  • assert_eq!(value1, value2) - tests the equality of two values. The test passes if they are equal.

  • assert!(value) - tests whether a value is true (or not). The test passes if the value within is true.

  • assert_ne!(value1, value2) - tests whether two values are not equal, the tests passes if two values are not equal.

Writing & running a test

The above test would work but is a bit bland. Let's import some external functions and run tests using cargo test. Similar to cargo run, cargo test is a command that only runs the functions specified as unit tests:

// The function we want to test
fn square(x: i32) -> i32 {
x * x
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn does_square_work() {
let squared = square(4);
assert_eq!(squared, 16);
}
}

Upon running cargo test, we can successfully see the test pass:

running 1 test
test tests::does_square_work ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

For more information regarding testing, it is highly encouraged to review the Rust Book's section on it.

- + \ No newline at end of file diff --git a/docs/Rust/setup/installation.html b/docs/Rust/setup/installation.html index 0414d2a57..848173935 100644 --- a/docs/Rust/setup/installation.html +++ b/docs/Rust/setup/installation.html @@ -5,14 +5,14 @@ Installing & Setting up a Rust Developer Environment | Polkadot Education Initiative - +

Installing & Setting up a Rust Developer Environment

In this section, you will:

  • Install Rust and its dependencies
  • Learn what rustup and cargo are
  • Get your local machine environment ready for Rust development

Setting up Your Environment

Rustup

There is a standard tool in the Rust ecosystem called rustup. This tool allows you to easily install and manage multiple Rust versions and use different Rust versions on a per-project basis. It is similar to a tool like nvm, the node version manager in the Javascript ecosystem. In addition to managing the version of Rust itself, it also installs cargo, the Rust build tool, and various language components such as different target architectures, and clippy, the Rust linting tool.

To install rustup, run this install script below:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

This is executing a script from the internet, so if you prefer to be careful, check its contents to ensure it isn't doing anything unwanted. This line of code installs several tools:

  • rustup, which was covered above,
  • cargo, the Rust package manager, and
  • rustc, the official compiler for the Rust language.

Executing the line of code above will prompt the installation options below:

Current installation options:


default host triple: x86_64-apple-darwin
default toolchain: stable (default)
profile: default
modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>

Select Option 1 for the standard install. We will add more components later in the course as needed. When the command completes, you can confirm that rustup is installed by checking its version with rustup --version:

$ rustup self update
info: checking for self-updates
rustup unchanged - 1.25.2

We can also check the version of the rust compiler and the cargo build tool.

$ rustc --version
rustc 1.68.0 (a55dd71d5 2022-09-19)

$ cargo --version
cargo 1.68.0 (387270bc7 2022-09-16)

This course will use version 1.68, but similar versions will work just as well.

You can update your toolchains at any time by running rustup update, and you can even update rustup itself by running rustup self update.

Visual Studio Code

The most commonly-used (and recommended) tool to use for Rust is Visual Studio Code (also called VS Code) with the Rust analyzer, which can be installed as a VS Code extension. Throughout the course, VS Code will be the primary editor of choice. Please head to https://code.visualstudio.com/download to install VS Code.

There are other acceptable text editors and integrated development environments also available. Atom IDE-Rust may also be used.

If you prefer an open source and freely-licensed distribution of VS Code that omits telemetry, VSCodium is also viable. See the project's installation site and the reason why you might want to use this over VS Code itself.

Extensions

Once VS Code is installed, there are a couple of extensions that will smooth the development experience.

The first extension we'll add is Rust Analyzer, a powerful static analysis tool for the Rust programming language. This extension provides some basics such as syntax highlighting and goto definition, as well as more powerful features such as code completion and documentation by hovering over code.

To install it, enter VS Code's command pallet with Ctrl + Shift + P, and paste:

ext install rust-lang.rust-analyzer

The second extension is optional but recommended if you like to do step debugging on your coding projects. It is the Code LLDB extension that provides seamless integration between LLDB, the low-level debugger, and VS Code. To install it, enter the command pallet again (Ctrl + Shift + P) and paste:

ext install vadimcn.vscode-lldb

We will not use this extension actively in this course, but it is a great tool to have handy when it comes time to debug your own code.

The Rust Playground

The Rust playground is an in-browser implementation of a Rust development environment. Feel free to use it to run snippets of code if setting up a proper development environment is somehow inaccessible. Throughout this course, you will notice it embedded within the page as follows:

You may also visit it at https://play.rust-lang.org/.

- + \ No newline at end of file diff --git a/docs/Substrate/section1.html b/docs/Substrate/section1.html index a8b41b147..082d9c8b5 100644 --- a/docs/Substrate/section1.html +++ b/docs/Substrate/section1.html @@ -5,13 +5,13 @@ Why Learn Substrate? | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Substrate/section1/capstone-project.html b/docs/Substrate/section1/capstone-project.html index 6d8aa963c..4b8d32f29 100644 --- a/docs/Substrate/section1/capstone-project.html +++ b/docs/Substrate/section1/capstone-project.html @@ -5,13 +5,13 @@ Course Capstone Project | Polkadot Education Initiative - +

Course Capstone Project

The purpose of this capstone project is to bring something both engaging and informative to the student. You, the student, should be able to build a Substrate pallet in the Substrate Node Template, run it, and modify and view its state in real time.

An honorable mention and inspiration behind this project are Substrate Kitties by Shawn Tabrizi.

Required Functionality - What You Will Build

  • Create an essential foundation for a Sybil-resistant social network with only profiles. The user must meet a set of prerequisites (balance) to join.
  • Teach how to count for Sybil attacks using other pallets.
  • Generating random profile pictures in the runtime.
  • Storing and managing user metadata and unique usernames. All handles are unique - no two usernames can be the same.
  • Learn only bound-constrained metadata for an account. Allow for a person to assign information about themselves, but limit how much information

Project Components

The following will be created as a result of this project:

  1. A runtime pallet, which the student can publish and modify as they will.
  2. An instance of the node template with this pallet integrated.
  3. A simple front end to display all profiles on a network.

Student Objectives

Note: the project should compile even without these!

  • Fill in a new event
  • Fill in a new dispatchable function
    • Interact with a storage map; both get a value, then set a new one.
    • Use ensure
  • Fill in a new error
  • Write a simple unit test to cover the new event
  • Show but not implement the use of Balances pallet

Substrate Topics Covered

The following Substrate topics and nuances will be covered as a result of this project:

  • Creating a FRAME pallet, adding it to the runtime, and configuring it for the runtime.
  • Learning how different pallet macros operate and work (dispatch, errors, events, storage).
  • Using the dev_mode macro to mitigate fees, but the concept of weight in Substrate will be discussed.
  • Going over the Rust language for each pallet concept.
  • Creating any storage mappings or associations for storing data in the chain
  • Learn how to utilize BoundedVec and the importance of bounded data in general in blockchain development.
  • Learning safe math via the Like or Dislike mechanism to prevent integer overflows. Explain the importance of safe math in the runtime.
  • Creating events for each action taken by a user
  • Learning the importance of error handling and not panicking within a FRAME pallet
  • Interact with pallet_balance through its associated trait, Currency, to check the balance and lock it if needed. Explain the use of BalanceOf<T> and how it works regarding Rust.
  • Loose vs. tight coupling, with loose coupling being used and prioritized.
  • Think through a realistic, real-world scenario of theory to a pallet.
- + \ No newline at end of file diff --git a/docs/Substrate/section1/substrate-design.html b/docs/Substrate/section1/substrate-design.html index 0a9912642..6f8c21abb 100644 --- a/docs/Substrate/section1/substrate-design.html +++ b/docs/Substrate/section1/substrate-design.html @@ -5,13 +5,13 @@ Substrate’s Design Choices | Polkadot Education Initiative - +

Substrate’s Design Choices

The Substrate code base was designed to be highly generic. This enables high customizability and definition of various primitives for creating a blockchain with Substrate.

From networking to defining how the state of a blockchain mutates, most of what makes Substrate adaptable comes from its heavy use of Rust's generic typing system.

What is a generic code base?

At a basic level, a generic code base represents a way to define very reusable code. The Substrate libraries use this design pattern to optionally leave some of the definitions of primitives up to the developer.

Blockchains often have standard 'primitives': Polkadot, Bitcoin, or Ethereum all have concepts and primitives that are commonplace, i.e., transactions, blocks, storage, and hashing algorithms. Substrate's generic nature allows for these blockchain primitives to be decided by the implementor.

We only really need to define these primitives one time for the chain. In FRAME, this is done using a Rust trait called Config, which uses associated types to group all relevant primitives together in a way defined only once for the entire blockchain.

What are the challenges?

There can be a degree of complexity for the developers maintaining a Substrate-based codebase due to its generic nature. If you are a developer working closely with Substrate libraries, then intimate knowledge of how this generic design (and subsequently, Rust's generic type system) works is highly useful.

Luckily with FRAME, abstractions are provided to mitigate this, allowing a developer to take full advantage of Substrate.

Further reading

- + \ No newline at end of file diff --git a/docs/Substrate/section1/substrate-history.html b/docs/Substrate/section1/substrate-history.html index 6ee85a5cf..b748bf4b2 100644 --- a/docs/Substrate/section1/substrate-history.html +++ b/docs/Substrate/section1/substrate-history.html @@ -5,13 +5,13 @@ History Behind Substrate | Polkadot Education Initiative - +
-

History Behind Substrate

Substrate, at its origins, was created very close to the conception of the Polkadot network. The two are usually tightly coupled - where the improvements to Substrate funnel into Polkadot and vice versa. Parachains that connect to Polkadot are also built using Substrate, generally using some abstractions such as FRAME and Cumulus.

Setting the context is crucial to understand Susbtrate's role in web3. Polkadot is a relay chain that uses Substrate libraries and FRAME. Substrate is a blockchain developer framework. It enables the creation of solo chains or parachains. Initially, much of what was Substrate came from Polkadot due to necessity. The reasoning behind this choice was if parachains were to be built, it would be sensible to make all standard blockchain protocols and primitives available to parachain developers.

- +

History Behind Substrate

Substrate, at its origins, was created very close to the conception of the Polkadot network. The two are usually tightly coupled - where the improvements to Substrate funnel into Polkadot and vice versa. Parachains that connect to Polkadot are also built using Substrate, generally using some abstractions such as FRAME and Cumulus.

Setting the context is crucial to understand Substrate's role in web3. Polkadot is a relay chain that uses Substrate libraries and FRAME. Substrate is a blockchain developer framework. It enables the creation of solo chains or parachains. Initially, much of what was Substrate came from Polkadot due to necessity. The reasoning behind this choice was if parachains were to be built, it would be sensible to make all standard blockchain protocols and primitives available to parachain developers.

+ \ No newline at end of file diff --git a/docs/Substrate/section1/what-is-substrate.html b/docs/Substrate/section1/what-is-substrate.html index 22e44551b..9e8ec947f 100644 --- a/docs/Substrate/section1/what-is-substrate.html +++ b/docs/Substrate/section1/what-is-substrate.html @@ -5,13 +5,13 @@ Substrate - powering Web3 | Polkadot Education Initiative - +

Substrate - powering Web3

Substrate is a modular framework that enables you to create domain-specific blockchains by composing custom or pre-built components. The Framework for Runtime Aggregation of Modularized Entities (FRAME) is a set of modules and support libraries that simplify runtime development. In Substrate, these modules are called pallets, each hosting domain-specific logic to include in a chain's runtime.

Substrate may be utilized to create a solo chain or a parachain for a relay chain like Polkadot or Kusama. At its core, it is a set of Rust crates that provide the most basic primitives and protocols to implement a distributed state machine, such as a blockchain.

An analogy: React, the library for web and native user interfaces

Another way to think about Substrate is similar to how a web developer may use/think about React. Where React provides core libraries for manipulating the elements of the DOM via developer-defined components, Substrate also provides the base libraries and primitives needed to create a blockchain. It was designed to not be opinionated via a highly generic codebase, enabling high customizability.

Substrate's Libraries

Substrate, as mentioned, is a collection of Rust crates that define a generic way to implement a blockchain. Although you will only use a few of these libraries directly in this course, know that everything you use is built upon them.

You will encounter two primary prefixes when developing with Substrate crates: sc- (Substrate Client) and sp- (Substrate Primitive). Crates prefixed with sc- usually refer to network-related functionalities, such as peer-to-peer networking or consensus. The sp- prefix usually indicates that the library provides primitives for a particular aspect of Substrate, such as runtime-related primitives.

A few significant crates that you will encounter are:

  • sp_runtime - Runtime modules shared primitive types
  • sp_core - Shareable Substrate types
  • sp_io - I/O host interface for substrate runtime. Substrate runtime standard library as compiled when linked with Rust’s standard library.

These, along with many more, all pertain to building certain aspects of a particular distributed system.

A Brief Introduction to FRAME (Framework for Runtime Aggregation of Modularized Entities)

A common question is: If Substrate exists, what is FRAME for? Why is it mentioned so often?.

FRAME, or Framework for Runtime Aggregation of Modularized Entities, is another abstraction on top of Substrate. It introduces a set of conventions and structures for building a blockchain with Substrate through numerous Rust macros. These macros enable the powerful and trivial to use a system of pallets to construct runtimes using Substrate. A runtime, also called a state transition function (STF), defines the state transitions for a particular blockchain.

info

As we advance, terminologies such as pallets, and custom bundles of business logic that make up a runtime will be expected. If a term here is unfamiliar and undefined, reference the glossary.

Remember, a state machine is the core concept of a blockchain. FRAME facilitates the way for state to propagate and change in a more developer-friendly way.

The outcome of developing on Substrate, particularly FRAME, is almost always a runtime (compiled to WebAssembly, or Wasm), also called a state transition function (STF). This runtime defines the core logic that determines how state propagates and changes in a blockchain. In the coming lessons, you will learn more about how a runtime works and eventually dive deeper into its structure.

- + \ No newline at end of file diff --git a/docs/Substrate/section2.html b/docs/Substrate/section2.html index 9dd8b0653..6e54d9415 100644 --- a/docs/Substrate/section2.html +++ b/docs/Substrate/section2.html @@ -5,13 +5,13 @@ Substrate 101 - Overview of the Substrate Framework | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Substrate/section2/substrate-pallets.html b/docs/Substrate/section2/substrate-pallets.html index 3b72dbb63..fb22405c7 100644 --- a/docs/Substrate/section2/substrate-pallets.html +++ b/docs/Substrate/section2/substrate-pallets.html @@ -5,13 +5,13 @@ Pallets | Polkadot Education Initiative - +

Pallets

FRAME, or the Framework for Runtime Aggregation of Modularized Entities, essentially heavily simplifies runtime development for us. It allows us to combine different runtime modules to customize our blockchain. Consensus, staking, governance, balances, and the like are all pallets covered under FRAME.

These modules are called pallets. In short, they contain the business logic for custom blockchain functionality and are added and managed by the runtime. In theory, you can build a Substrate based chain without them. However, they contain a lot of core functionality that serves as a valuable base for a new Substrate based chain.

We’ll create a custom pallet later in this course, where you will see how to mix and match pallets to create your own blockchain. The pallets handle the signing and submission of extrinsics on Substrate, allowing us to implement custom functionality directly into the chain.

Common Pallets in FRAME

Several standard pallets built using FRAME are used in many networks and relay chains like Polkadot or Kusama. The standard pallet naming convention is pallet_ followed by the actual name. Here are a few standard pallets that you might often encounter as a developer:

  • pallet_balances - The Balances pallet provides account and balance functionality.
  • pallet_system - The System pallet provides low-level access to core types and cross-cutting utilities. It is the base layer for other pallets interacting with the Substrate framework components.
  • pallet_referenda - The Referenda pallet handles the administration of general stakeholder voting. This pallet along with other pallets like pallet_conviction_voting drives Polkadot OpenGov - the next generation of decentralized on-chain governance.

To view all pallets included with FRAME, look at the repository on GitHub.

Our Pallet: pallet_connect

As a part of this course, the pallet you will build and modify is called pallet_connect. It is a basis for a simple, Sybil-resistant social network, demonstrating several vital functionalities and practices for building pallets using FRAME.

Below you may find a list of the exact features this pallet provides:

  • Create the equivalent of a social network, but with only profiles. The user must meet a set of prerequisites (balance) to join.
  • Account for Sybil attacks by requiring an amount of currency to be locked (an existential deposit) for each user.
  • Each profile picture is generated on-chain using on-chain randomness - forming a gradient image associated with every user.
  • All handles are unique. No two usernames can be the same.
  • Bounded metadata for each account.
- + \ No newline at end of file diff --git a/docs/Substrate/section2/substrate-runtime.html b/docs/Substrate/section2/substrate-runtime.html index b6e450070..f30667475 100644 --- a/docs/Substrate/section2/substrate-runtime.html +++ b/docs/Substrate/section2/substrate-runtime.html @@ -5,13 +5,13 @@ Runtime & Outer Node | Polkadot Education Initiative - +

Runtime & Outer Node

The runtime is the heart of our blockchain. It defines precisely how our blockchain will handle new data, whether it will react to a change in the network's state, and communicates with storage and networking layers as needed. Each runtime is a Wasm Virtual Machine. It is possible to update runtime by modifying/replacing Wasm VM, which enables forkless upgrades on Substrate Blockchains to be possible.

It is also responsible for ensuring the validity of extrinsics, can compile Wasm code for forkless upgrades, and, most importantly for us — facilitates the usage of FRAME (Framework for Runtime Aggregation of Modularized Entities) to build and add pallets. You can think of the runtime as the engine that facilitates the outer node (that ends up interacting with other nodes over the network) and storage to communicate, process, then store changes.

note

Because the runtime compiles to a Wasm environment, it is also a no_std environment. For this reason, not all external Rust crates will work with a runtime unless they can work in a no_std environment.

The Outer Node

The outer node refers to the portion of a Substrate node that deals with peer-to-peer networking and receiving new incoming from nodes. Using host functions, it can pass this data as an opaque blob that is then decoded and processed by the runtime, where it is then stored in the underlying storage layer.

- + \ No newline at end of file diff --git a/docs/Substrate/section2/substrate-storage.html b/docs/Substrate/section2/substrate-storage.html index e30832334..930f91b82 100644 --- a/docs/Substrate/section2/substrate-storage.html +++ b/docs/Substrate/section2/substrate-storage.html @@ -5,13 +5,13 @@ Storage | Polkadot Education Initiative - +

Storage

When building a custom pallet, storage of some kind will be needed. As with any other application, we need a place to store information after submitting state changes to the network. Whether we’re looking to store a simple value or complex data structures with mappings, a set of APIs are provided to implement it accordingly. Because the state is stored within this storage and replicated across the network, it is essential to realize that storage via blockchain is considered costly and should be used wisely by any pallet in the runtime.

Fun Fact: Polkadot and Kusama's Runtime is 1.5 MB

The runtime itself is stored using this storage instance of the Substrate node under a unique key within the underlying key-value database. The entirety of the runtime's Wasm code can be accessed on-chain.

FRAME storage items allow a developer to define and store new items in our blockchain’s state. Substrate's underlying sp_io is responsible, where they make calls accordingly to the storage layer of Substrate.

Substrate Storage Overview

Although FRAME makes declaring and using different storage primitives trivial, it's important to understand some context for what is occurring at a slightly lower level.

If you recall, the sp_io crate handles any needed I/O operations for the runtime. In a theoretical scenario (such as FRAME not being used or available), sp_io's underlying APIs could be called directly. This crate is not just simply logging to some arbitrary database - there are several layers to the storage of a Substrate node. This is out of the scope of this course, but it is prudent to know just how abstract some of the mechanisms in Substrate are.

FRAME Storage Abstraction

There are several ways to declare storage using FRAME. These are done by using several storage items, which are summarized below:

  • StorageMap - A type that stores value for a given key. Allowing to insert/remove/iterate on values.
  • StorageValue - A type that allows storing a single value.
  • StorageDoubleMap - A type that allows storing values for (key1, key2) couple. Similar to StorageMap, but allow to iterate and remove value associated with the first key.
  • StorageNMap - A type that allows to store values for an arbitrary number of keys in the form of (Key<Hasher1, key1>, Key<Hasher2, key2>, ..., Key<HasherN, keyN>).

For more information on their usage and APIs, look at the Substrate Rust documentation for further info.

Further in the course, you will see StorageMap and StorageValue being utilized.

- + \ No newline at end of file diff --git a/docs/Substrate/section3.html b/docs/Substrate/section3.html index c02e88ed7..0c367e5b5 100644 --- a/docs/Substrate/section3.html +++ b/docs/Substrate/section3.html @@ -5,13 +5,13 @@ Substrate Node Template | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Substrate/section3/explore-pallet-template.html b/docs/Substrate/section3/explore-pallet-template.html index 9a047f874..f43d34826 100644 --- a/docs/Substrate/section3/explore-pallet-template.html +++ b/docs/Substrate/section3/explore-pallet-template.html @@ -5,13 +5,13 @@ Exploring the pallet template | Polkadot Education Initiative - +

Exploring the pallet template

We will spend most of our time in the pallets/connect folder in the next modules. Upon opening it, there are a few items of interest, mostly with testing, as well as the actual source code of the pallet. Again, as mentioned in the last section of this module, this is just a Rust crate that uses FRAME to generate and implement certain traits and structures to be compatible with the runtime.

Our pallet, called pallet-connect, defines our custom logic that is then enabled by our runtime within the node template.

lib.rs

lib.rs defines the entry point for the pallet. It contains all the core logic - extrinsics, events, and errors, are all defined by a single struct:

#[pallet::pallet]
pub struct Pallet<T>(_);

We will go into more detail on the structure of lib.rs later but know for now that this makes a FRAME pallet at the most basic level. Pallets, just like this one, are later imported as a crate to the runtime, where the aforementioned construct_runtime! macro is responsible for defining and constructing a runtime based on the collection of pallets and their respective configurations.

mock.rs and tests.rs

In most pallets, including our template, you will also see the following files: mock.rs and tests.rs. As one may assume, they are used for two purposes:

  • mock.rs is used for configuring a test environment, i.e., a test runtime configured for unit testing.
  • tests.rs is where unit tests reside and act on the values and configuration defined within mock.rs

benchmarking.rs and weights.rs

Benchmarking and weights measure an extrinsic's performance, or weight. Substrate is built around the concept of weight, which measures how much computation is required to execute it on-chain. Using benchmarking, weight can be assigned to each extrinsic, and the weights.rs file is generated. Often, this weight corresponds to how much a user will pay to execute that state change. Weights also ensure that the transactions within a block can be executed successfully within the block production time window.

Although these two won't be covered in this course in-depth at a technical level, they are both crucial concepts to learn if one wishes to create a production-grade pallet.

- + \ No newline at end of file diff --git a/docs/Substrate/section3/install-deps.html b/docs/Substrate/section3/install-deps.html index 8f7a29177..4a67f4a7b 100644 --- a/docs/Substrate/section3/install-deps.html +++ b/docs/Substrate/section3/install-deps.html @@ -5,13 +5,13 @@ Install, configure, and run the Node Template | Polkadot Education Initiative - +

Install, configure, and run the Node Template

It’s time to download the Substrate Node Template. This repository will give us everything to run, test, and create a custom FRAME-based pallet.

As a part of this course, a pre-configured pallet and node runtime are already provided for you, which you will find here.

Cloning & Building

You should already have the Rust toolset installed on your system. If not, go ahead and follow the instructions to install and come back to this section. Remember, this may take a bit to build and run, so feel free to get a cup of coffee or read an awesome Medium article while you wait.

Remember that this repo is a clone from the original substrate-node-template, but with a few tweaks to make it easier to work with when we start our pallet development.

git clone https://github.com/w3f/substrate-mooc-node
cd substrate-mooc-node/
# This will build and launch the node
# If you wish to just build it, then run cargo build --release
cargo run --release -- --dev

Once built, we have multiple ways of running and interacting with our node.

Running & Viewing our Development Chain

For immediate results to ensure our chain is up, go ahead and visit the Polkadot.js Explorer:

Polkadot/Substrate Portal

This link will automatically connect to your localhost node, where you can view all chain stats. For example, accounts, their balances, blocks, and on-chain events:

Polkadot JS Dev

Observing Chain State

By navigating to Developer > Chain State, it is possible to get the state of various storage mappings or values previously defined by the pallets in the runtime. These are called State Queries.

For example, you can select the timestamp state query and click the plus button on the far right to get the time for the node:

Modifying Chain State

Modifying the chain's state is done via extrinsics . An extrinsic is similar to the concept of a transaction, as it represents a unit of change within a chain's particular state, i.e., transferring currency that changes two users' balances.

You can also simply search storage by raw hexadecimal key. However, most of the time, it’s easier to perform state queries via the respective pallet and its associated storage items.

Using the substrate-frontend-template

While the explorer is an ideal place for general functions, the substrate-frontend-template is another way to interact with the node.

Navigate to where you installed it, and run yarn start (assuming you have run yarn install to install its local dependencies):

This should launch the frontend, located at localhost:8000:

Substrate Frontend Template

You now have most functionality and access to your chain through a single-page GUI. For example, you can use the transfer pallet to transfer currency between accounts, upgrade your runtime via a forkless upgrade, and interact with pallets to modify the state of the chain directly.

Feel free to play around here and experiment as much as possible with this interface, as a lot can be gleaned from observing this structure. If you notice in the dropdown — one of the pallets is called connect.

On the next page, we’ll modify and go through this pallet to make it our own.

- + \ No newline at end of file diff --git a/docs/Substrate/section3/install-explore-frontend.html b/docs/Substrate/section3/install-explore-frontend.html index c2715af12..90be1cc5c 100644 --- a/docs/Substrate/section3/install-explore-frontend.html +++ b/docs/Substrate/section3/install-explore-frontend.html @@ -5,13 +5,13 @@ Installing & exploring the frontend template | Polkadot Education Initiative - +

Installing & exploring the frontend template

The front-end template is a valuable source that will allow us to quickly verify and test our Substrate chain with minimal interference.

Installation

# Clone the repository
git clone https://github.com/substrate-developer-hub/substrate-front-end-template.git
cd substrate-front-end-template
yarn install

# To start it
yarn start

More info can be found at the repository.

The Polkadot.js Explorer

The Polkadot.js explorer is also a great way for developers to get more insight into your node’s operations:

Polkadot JS Main

The image above shows the live telemetry from the Polkadot relay chain.

Once there, you can click the upper left, select Development at the bottom of the menu, select Local Node, and click Switch in the top right:

Polkadot JS Switch

Optionally, this link will also take you directly to it, although it won't show anything if a local node is not running.

- + \ No newline at end of file diff --git a/docs/Substrate/section3/node-template-tour.html b/docs/Substrate/section3/node-template-tour.html index fbc8c8f0a..aa1ed0bf8 100644 --- a/docs/Substrate/section3/node-template-tour.html +++ b/docs/Substrate/section3/node-template-tour.html @@ -5,13 +5,13 @@ Node Template Tour & Overview | Polkadot Education Initiative - +

Node Template Tour & Overview

In the last section of this module, you installed and ran the substrate-mooc-node. It's important to realize that this isn't just a node for a blockchain but an entire blockchain itself. If you look further into the configuration and structure of this node template, some files are specifically for setting the initial state of the entire blockchain.

Node Template Structure

The substrate-mooc-node is based on the substrate-node-template, "A Substrate project such as this consists of several components spread across a few directories."

It comes pre-configured with several crucial components to develop a blockchain with FRAME and Substrate - including networking, configured runtimes, and a template for adding more pallets to your chain. Pallets can be added either locally, as per this example, or remotely. For now, pallets are imported as crates from GitHub (using the git = "<source>" within the Cargo.toml of a particular project).

Node / Networking Directory - node/

To allow your chain to communicate to the outside world, whether to other nodes in the chain or to expose an RPC interface. It must contain networking to perform peer-to-peer actions for functionality like finality, which are included within Substrate as a library (sc_consensus_grandpa). There are also a few other practical files, such as cli.rs, which exposes a command-line interface for your node.

Paraphrasing the node template, a blockchain node typically has these three things (which are included with this node):

  1. Networking - This is implemented with libp2p and Substrate libraries and is how nodes communicate in a multi-node scenario.
  2. Consensus - Consensus is the crucial aspect allowing the chain to agree on some state. Substrate provides some implementations for consensus models, i.e., sc_consensus_grandpa, but also provides a way to create your consensus model.
  3. RPC Server - A remote procedural call server that enables clients to interact with the node.

It has three primary files that implement the above:

  • service.rs - Defines each related service needed to establish peer-to-peer communications, consensus, finalization, and fork-choice ruling.
  • rpc.rs - Manages and keeps track of clients and their requests, extendable by API if the developer wishes (and the pallet exposes methods for the RPC to utilize).
  • chain_spec.rs - The chain specification file establishes an initial configuration for the state of the network, such as user balances on genesis (for both development and production).

Runtime Directory - runtime/

The runtime, also referred to as the state transition function, refers to the core business logic at the center of the blockchain. It is the beating heart of the blockchain node and the deciding factor in how a piece of data is transformed and stored and affects another state within the chain. It is also responsible for validating blocks, probably one of the best examples of this occurring.

It is within this runtime that you will see FRAME being utilized completely. FRAME, as mentioned, provides many support libraries (and their respective macros therein) that are used for building runtimes. As also mentioned, pallets are used to establish a custom, domain-specific pieces of logic that can be added to the runtime.

It has one primary file to look at: runtime/lib.rs.

Pallets Subdirectory = pallets/

Lastly, the pallets/ can be used to add or create pallets locally as a Rust crate. However, pallets are often added remotely within runtime/Cargo.toml. Pallets can also use other pallets through a coupling mechanism, which you will learn later.

All pallets are Rust crates that utilize FRAME's support libraries and macros, where they are then registered and configured within the runtime.

- + \ No newline at end of file diff --git a/docs/Substrate/section4.html b/docs/Substrate/section4.html index c51da2647..03d1e46ac 100644 --- a/docs/Substrate/section4.html +++ b/docs/Substrate/section4.html @@ -5,13 +5,13 @@ Custom FRAME Pallet | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Substrate/section4/create-storage-map.html b/docs/Substrate/section4/create-storage-map.html index baeed5bad..adfe053c7 100644 --- a/docs/Substrate/section4/create-storage-map.html +++ b/docs/Substrate/section4/create-storage-map.html @@ -5,13 +5,13 @@ Creating storage maps | Polkadot Education Initiative - +

Creating storage maps

info

For this section, you should be in: pallets/connect/lib.rs to follow along.

This section will focus on adding a new FRAME storage item to pallet_connect. With our events and errors defined, we have two items left on our action list:

  1. Implement events and errors
  2. Implement storage items
  3. Implement dispatchable functions (extrinsics)

Defining a Storage Item

While we have errors to define user as out of bounds and events that emit when someone successfully registers, there is currently no place to store the data about the user in underlying storage.

There are several types of storage items in FRAME. We will use a StorageMap, which works similarly to a HashMap in principle. Navigate to the struct called UserMetadata, and right under it, add this type alias:

    /// Registered users mapped by address
#[pallet::storage]
#[pallet::getter(fn registered_users)]
pub type RegisteredUsers<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, UserMetadata<T>, OptionQuery>;

This storage item maps the AccountId from Config (hence T::AccountId) to the struct UserMetadata. The #[pallet::storage] defines and expands this type alias for us.

A few notes about this storage item:

  • Blake2_128Concat refers to how the keys are hashed, referring to the Blake2_128 hashing algorithm. This is a secure hashing algorithm.
  • T::AccountId is the key to access the value, UserMetadata<T>.
  • The generic T must implement the Config trait. This gives it access to overarching types, such as core primitives (i.e., AccountId).
  • OptionQuery refers to how to handle when the value is retrieved. OptionQuery works by returning an Option<T>, meaning a user can either be Some(UserMetadata) or None.
- + \ No newline at end of file diff --git a/docs/Substrate/section4/events-errors.html b/docs/Substrate/section4/events-errors.html index a1b4cffe0..1dc41b0c9 100644 --- a/docs/Substrate/section4/events-errors.html +++ b/docs/Substrate/section4/events-errors.html @@ -5,13 +5,13 @@ Events & Errors | Polkadot Education Initiative - +

Events & Errors

info

For this section, you should be in: pallets/connect/lib.rs to follow along.

Although our node and pallet compile, it does not contain all the functionality we need to fulfill the project's requirements.

We have three overarching components we need to take care of to fulfill our functionality:

  1. Implement events and errors
  2. Implement storage items
  3. Implement dispatchable functions (extrinsics)

This section will focus on adding new events and errors to pallet_connect to prepare for creating state changes.

Defining Events - Adding a New Event

By now, your working directory should be pallets/connect/lib.rs. Navigate to enum Event<T> inside of lib.rs:

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

#[pallet::generate_deposit(pub(super) fn deposit_event)] is a macro that we haven't covered yet. It simply defines a helper method to deposit or emit an event.

Considering the project's requirements, we would like to emit an event whenever a user registers. We can add Registered to define a new event as a variant of our Event enum. As part of this event, we also would like to show the AccountId, or address, of the registered user. The implementation is as follows:

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A new user has registered via our pallet.
Registered { id: T::AccountId },
}

Later, we can emit this event to the network once a user registers.

Defining Errors - Adding a New Error

As a part of our pallet's anti-sybil requirements, we also want to only register a user if they have enough balance to place a lock on. However, if they don't have enough balance, we do not want the extrinsic to commit to a state change.

We shouldn't panic within our pallet, meaning we must define an error to signify when someone has a low balance.

Navigate to enum Error, and an error variant called LowBalance:

    // Errors inform users that something went wrong.
#[pallet::error]
pub enum Error<T> {
/// Balance does not meet the minimum required amount
LowBalance,
}

Because we also have other requirements, be sure to also add these errors:

    // Errors inform users that something went wrong.
#[pallet::error]
pub enum Error<T> {
/// Balance does not meet the minimum required amount
LowBalance,
/// Name exceeds MaxNameLength
NameTooLong,
/// Bio exceeds MaxBioLength
BioTooLong,
/// Name already registered
NameInUse,
/// Account ID is already registered
AccountIdAlreadyRegistered,
/// Integer overflow
IntegerOverflow,
}

Now we have a set amount of errors that cover any cases where our extrinsic may fail.

How many events should I emit? How many errors should I define?

In pallet development, it's highly recommended to define errors for every edge case where a state change may fail. The runtime should not panic, ever. It is prudent to define and handle appropriate errors within your pallet.

Events do not need to be as often, but only as it is useful. Generally, an event can be emitted whenever an extrinsic is successfully executed.

- + \ No newline at end of file diff --git a/docs/Substrate/section4/pallet-config.html b/docs/Substrate/section4/pallet-config.html index ae2afa24b..222f60165 100644 --- a/docs/Substrate/section4/pallet-config.html +++ b/docs/Substrate/section4/pallet-config.html @@ -5,13 +5,13 @@ Adjusting Pallet Config & Runtime Overview | Polkadot Education Initiative - +

Adjusting Pallet Config & Runtime Overview

Before looking at our pallet (pallet_connect), let's tour the node's runtime - runtime/lib.rs and change our pallet's configuration.

Adjusting pallet_connect's Config

Upon looking at the file, it is evident that each pallet must be configured. pallet_connect is no different and requires several configuration parameters pertaining to its functionality:

/// Configure pallet-connect
impl pallet_connect::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_connect::weights::SubstrateWeight<Runtime>;
type Currency = Balances;
type MaxBioLength = ConstU32<100>;
type MinimumLockableAmount = MinimumLockableAmount;
type MaxNameLength = ConstU32<100>;
type Randomness = InsecureRandomness;
}

The ones to pay attention to for now are MaxBioLength and MaxNameLength. These define bounded data types in the pallet and are configurable via the runtime.

Go ahead and change MaxBioLength to a higher limit:

/// Configure pallet-connect
impl pallet_connect::Config for Runtime {
...
// Increase from 100 to 200.
type MaxBioLength = ConstU32<200>;
...
}

Congratulations, you just changed your first runtime configuration parameter!

runtime/lib.rs Overview

If you navigate to this file, it may seem intimidating at first. The node's runtime defines all the business logic, or state transition function, for how an extrinsic may change the chain's state. This runtime is built using FRAME and takes advantage of several macros and conventions used to implement various pallets, as well as the runtime itself.

The runtime houses the configuration for not only itself but also the pallets that it utilizes.

impl frame_system::Config for Runtime and struct Runtime

This implementation defines several configurable factors for a FRAME-based runtime. It uses associated types, as discussed previously, to determine common types for blockchain-based primitives. For example, for this runtime, an AccountId is defined as follows:

/// Some way of identifying an account on the chain.  We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;

impl frame_system::Config for Runtime {
...
/// The identifier used to distinguish between accounts.
type AccountId = AccountId;
...
}

Note that the common type is often defined via a type alias at the top of runtime/lib.rs.

Pallet Configurations

Following the primary configuration of the overall runtime, it is also necessary to configure each pallet. This is done by implementing the pallet's Config trait for the runtime. For example, the sudo pallet (pallet_sudo) is a simple configuration where it simply utilizes the runtime's Event and Call types:

impl pallet_sudo::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
}

construct_runtime! macro

This macro is what is responsible for registering pallets that were configured. It is what creates the runtime with the pallets and name given (in this case, Runtime):

construct_runtime!(
pub struct Runtime
where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system,
InsecureRandomness: pallet_insecure_randomness_collective_flip,
Timestamp: pallet_timestamp,
Aura: pallet_aura,
Grandpa: pallet_grandpa,
Balances: pallet_balances,
TransactionPayment: pallet_transaction_payment,
Sudo: pallet_sudo,
// Include the custom logic from the pallet-connect in the runtime.
Connect: pallet_connect,
}
);
- + \ No newline at end of file diff --git a/docs/Substrate/section4/project-structure.html b/docs/Substrate/section4/project-structure.html index 66f9dc68c..39a7db0ae 100644 --- a/docs/Substrate/section4/project-structure.html +++ b/docs/Substrate/section4/project-structure.html @@ -5,13 +5,13 @@ lib.rs Structure Deepdive | Polkadot Education Initiative - +

lib.rs Structure Deepdive

In the previous module, we did take a brief tour of the pallet under pallets/connect. We learned that a pallet is just a Rust crate, which uses FRAME and its support libraries and macros to create a valid pallet for a FRAME-based runtime. This lesson will take a deeper dive into how some of these macros work, including the overall structure of a pallet.

Crucial Macros

If we strip away the logic within pallet_connect, we come to see a bare lib.rs:

#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
pub use weights::*;

#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

#[pallet::pallet]
pub struct Pallet<T>(_);

#[pallet::config]
pub trait Config: frame_system::Config {}

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

#[pallet::error]
pub enum Error<T> {}

#[pallet::call]
impl<T: Config> Pallet<T> {}

Top-down, there are a few important macros to pay attention to:

  • #[frame_support::pallet] - The pallet attribute macro defines a pallet that can be used with construct_runtime!.
  • #[pallet::pallet] - The pallet struct placeholder, #[pallet::pallet] is mandatory and allows you to specify pallet information.
  • #[pallet::config] - The mandatory attribute #[pallet::config] defines the configurable options for the pallet.
  • #[pallet::event] - The #[pallet::event] attribute allows you to define pallet events.
  • #[pallet::error] - The #[pallet::error] attribute allows you to define an error enum that will be returned from the dispatchable when an error occurs.
  • #[pallet::call] - The #[pallet::call] attribute allows you to define ways to dispatch an extrinsic.

Each procedural macro generates and expands a piece of code related to the pallet. In terms of Rust, you can see that the pallet is first declared as a module, then there is a struct, Pallet, which is the revolving type that this pallet uses to apply its subsequent Config trait. Two important enums, namely Event and Error, are also available. At a high level, these simply define pallet-specific events or errors.

Pallet Configuration - Config<T>

A common pattern you may notice is a seemingly ambiguous generic type: T. If you are unfamiliar with generic types and their usage in Rust, below is a quick explanation of how they work in this context.

Use of generics and associated types

Remember that Substrate is built to be generic, saving the developer the pains of implementing blockchain-related primitives from scratch. FRAME and Substrate both make use of two crucial Rust-related concepts:

T refers to a generic or placeholder type. Where macros enable a relatively easy experience implementing various mandatory functionality, generics allow the code to be highly configurable. The Config trait uses generics to provide a generic implementation of an overarching configuration which may derive many blockchain primitives therein.

- + \ No newline at end of file diff --git a/docs/Substrate/section5.html b/docs/Substrate/section5.html index f6b88703a..53805ae8d 100644 --- a/docs/Substrate/section5.html +++ b/docs/Substrate/section5.html @@ -5,13 +5,13 @@ Building a FRAME Pallet | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Substrate/section5/coupling-pallets.html b/docs/Substrate/section5/coupling-pallets.html index 349038586..bc567b043 100644 --- a/docs/Substrate/section5/coupling-pallets.html +++ b/docs/Substrate/section5/coupling-pallets.html @@ -5,13 +5,13 @@ Using other pallets | Polkadot Education Initiative - +

Using other pallets

We successfully constructed a function that registers a user in the previous section. Within that function, we used the T: Config generic to access two noteworthy traits:

  • Currency - i.e., T::Currency
  • Randomness - i.e., T::Randomness

Softly coupling pallets via Config

Both of these allowed us to check user balances with Currency and a source of randomness with Randomness. Taking a closer look, these are both in our pallet's configuration. The act of including a pallet's functionality within the configuration is called soft coupling:

/// Configure the pallet by specifying the parameters and types it depends on.
#[pallet::config]
pub trait Config: frame_system::Config {
/// Using the pallet_balances exposed 'Currency' trait to fetch user balance info
type Currency: ReservableCurrency<Self::AccountId> + LockableCurrency<Self::AccountId, Moment = Self::BlockNumber>;
/// Randomness!
type Randomness: Randomness<Self::Hash, Self::BlockNumber>;
}

Each of these traits interfaces with other pallets. ReservableCurrency and LockableCurrency are both traits of the pallet_balances and give us the ability to do things such as lock balance, check balance, and whatever other methods the trait exposes.

Randomness is a more straightforward example and also accomplishes the same concept. Any pallet that implements the Randomness trait can now be used within our pallet's configuration, just as the ReservableCurrency and LockableCurrency traits can also be utilized with any pallet that exposes these as interfaces.

Calling the Balances and Randomness pallet within register

Because both traits are part of our Config, we can access these types using the generic T. This is possible due to how the Pallet struct must take some generic T which implements Config as a trait:

/// The extrinsics, or dispatchable functions, for this pallet.
#[pallet::call]
impl<T: Config> Pallet<T> { ... }

We can now access any type within our configuration, per T: Config.

Type breakdown: BalanceOf<T>

At the type of the file, you notice a type alias that uses the Currency trait as well:

type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

This type of declaration defines our minimum lockable amount for a user. In short, it correctly states what balance should look like, as per the currency system (in this case, whatever Currency is) in place and used in the rest of the chain.

- + \ No newline at end of file diff --git a/docs/Substrate/section5/dispatchable.html b/docs/Substrate/section5/dispatchable.html index 383cad3e4..7b9ede37d 100644 --- a/docs/Substrate/section5/dispatchable.html +++ b/docs/Substrate/section5/dispatchable.html @@ -5,13 +5,13 @@ Creating dispatchable functions | Polkadot Education Initiative - +
-

Creating dispatchable functions

With all of our tools in place to register a user, let's go through the process of adequately registering them via an extrinsicor state change.

What is an extrinsic again?

An extrinsic is a transaction or a unit to define how the state should change within the network.

Adding the register dispatchable

Navigate to the #[pallet::call] macro; it should be empty:

#[pallet::call]
impl<T: Config> Pallet<T> {
// Our dispatchable goes here.
}

We are going to create a function called register, which will take several parameters/factors into consideration and perform the following checks:

  • Take a parameter, name, of the user. The character amount must be below MaxNameLength.
  • Take a parameter, bio, of the user. The character amount must be below MaxBioLength.
  • Check if they have enough balance to lock, and if so, lock it. Else, they cannot register.
  • Generate a profile picture for our user
  • If the user meets the requirements, we store them in our StorageMap.
  • Emit an event that they registered.

Defining our sender and function

With our requirements adequately defined, we can begin coding this function. Go ahead and paste the function called register that includes some beginning logic to start:

#[pallet::call]
impl<T: Config> Pallet<T> {

/// Registers a user to the network. It requires the balance of the sender to have an amount
/// which is greater than or equal to MinimumLockableAmount. Locks MinimumLockableAmount as
/// part of the registration process.
#[pallet::call_index(0)]
pub fn register(origin: OriginFor<T>, name: Vec<u8>, bio: Vec<u8>) -> DispatchResult {
let sender = ensure_signed(origin)?;
}
}

Checking balance and using ensure! to check requirements

A very useful macro, ensure!, is provided by FRAME. This macro enables us to check a condition. If the condition proves false, it allows an extrinsic to fail with a specific error.

We can also use the Currency trait included with our configuration. We will elaborate more on this trait later but know that for now, it enables us to check the balance of the sender:

// Retrieve the "free" balance of the user
let balance = T::Currency::free_balance(&sender);

// Before proceeding - we have to make sure the *free* balance of a user is enough to
// lock up! Otherwise, we halt this dispatchable with an error.
ensure!(balance >= T::MinimumLockableAmount::get(), Error::<T>::LowBalance);

Unbounded to bounded

If you notice, the parameters (name and bio) provided in register are of type Vec<u8>. This is a vector of bytes, which you may now consider a String.

Within our config, we have two notable constants defined: MaxBioLength and MaxNameLength. We want our two parameters to be bounded to these limits, as we shouldn't allow for infinite values to be stored on the chain.

The following code does just that and maps the appropriate error if it does exceed this length. We use the type BoundedVec to convert from a Vec to something that is bounded:

let name_bounded: BoundedVec<u8, T::MaxNameLength> =
BoundedVec::try_from(name.clone()).map_err(|_| Error::<T>::NameTooLong)?;
let bio_bounded: BoundedVec<u8, T::MaxBioLength> =
BoundedVec::try_from(bio).map_err(|_| Error::<T>::BioTooLong)?;

// 2. Check if the name already exists or user metadata already exists
ensure!(<Names<T>>::get(&name_bounded).is_none(), Error::<T>::NameInUse);
ensure!(
<RegisteredUsers<T>>::get(&sender).is_none(),
Error::<T>::AccountIdAlreadyRegistered
);

Generate a gradient profile and build our user

We can build our user once we have our parameters converted and ready.

Firstly, we can call another trait, T::Randomness, to provide a random value to the included generate_hex_values function.

This will return two randomly generated hex values that can be used to create a gradient profile picture:

// Generate our random profile picture (aka, two hex values that form a gradient)
// Usually, some increasing nonce is used as a seed. For simplicity, we use the account
// id as the seed.
let (value, _) = T::Randomness::random(&sender.encode());
let random_pfp = Self::generate_hex_values(value);

// Construct our UserMetadata. Ideally, we could also create an implementation to make
// this easier to create!
let user_metadata: UserMetadata<T> = UserMetadata {
name: name_bounded.clone(),
bio: bio_bounded,
profile_gradient: random_pfp,
account_id: sender.clone(),
};

Lock balance and store our user

With our users fully configured, we can now lock their balance and finish the registration process by storing them in our RegisteredUsers mapping:

We also add the user's name to another mapping of names (Names) to ensure it doesn't get taken.

// Lock the minimum deposit.  This account will now have this amount locked until
// they 'de-register'.
T::Currency::set_lock(
LOCK_ID,
&sender,
T::MinimumLockableAmount::get(),
WithdrawReasons::RESERVE,
);

// Store the user, add to existing names, and update the total amount of users
<RegisteredUsers<T>>::insert(&sender, user_metadata);
<Names<T>>::insert(&name_bounded, sender.clone());

Update the total amount of users on the network

Once we register the user, we can update the total number of users on the network. Note the use of checked_add and unwrap_or_default():

// Note the use of 'unwrap_or_default' - this is better than just a plain 'unwrap()'
// The default for 'u32' is 0, meaning an 'unwrap_or(0)' could also work here!

let total_registered = <TotalRegistered<T>>::get().unwrap_or_default();

// Using checked_add() ensures 'safe math' occurs.
// Since we never want panic within a runtime, we have to ensure all *possible* errors
// can be caught.

<TotalRegistered<T>>::put(
total_registered.checked_add(1).ok_or(Error::<T>::IntegerOverflow)?,
);

Emit an event

Lastly, we can emit an event once everything above is performed to indicate a new user has been registered:

// Emit an event to indicate a new user was added to the network
Self::deposit_event(Event::Registered { id: sender });

Full register function

The entire register function should end up looking like this by the end:

    #[pallet::call_index(0)]
pub fn register(origin: OriginFor<T>, name: Vec<u8>, bio: Vec<u8>) -> DispatchResult {
let sender = ensure_signed(origin)?;
let balance = T::Currency::free_balance(&sender);

// Before proceeding - we have to make sure the *free* balance of a user is enough to
// lock up! Otherwise, we halt this dispatchable with an error.
ensure!(balance >= T::MinimumLockableAmount::get(), Error::<T>::LowBalance);

// 1. Craft the user metadata out of the given parameters from `register`.
// Keep in mind we have to cast these to `BoundedVec` using the limits we have defined
// in our Config (hence why we must access them using our handy `T` generic operator!).
// Notice the error handling! Other types of error handling are okay too :)

let name_bounded: BoundedVec<u8, T::MaxNameLength> =
BoundedVec::try_from(name.clone()).map_err(|_| Error::<T>::NameTooLong)?;
let bio_bounded: BoundedVec<u8, T::MaxBioLength> =
BoundedVec::try_from(bio).map_err(|_| Error::<T>::BioTooLong)?;

// 2. Check if the name already exists or user metadata already exists
ensure!(<Names<T>>::get(&name_bounded).is_none(), Error::<T>::NameInUse);
ensure!(
<RegisteredUsers<T>>::get(&sender).is_none(),
Error::<T>::AccountIdAlreadyRegistered
);

// 3. Generate our random profile picture (aka, two hex values which form a gradient)
// Usually, some increasing nonce is used as a seed. For simplicity, we use the account
// id as the seed.
let (value, _) = T::Randomness::random(&sender.encode());
let random_pfp = Self::generate_hex_values(value);

// 4. Construct our UserMetadata. Ideally, we could also create an implemention to make
// this easier to create!
let user_metadata: UserMetadata<T> = UserMetadata {
name: name_bounded.clone(),
bio: bio_bounded,
profile_gradient: random_pfp,
account_id: sender.clone(),
};

// 5. Lock the minimum deposit. This account will now have this amount locked until
// they 'de-register'.
T::Currency::set_lock(
LOCK_ID,
&sender,
T::MinimumLockableAmount::get(),
WithdrawReasons::RESERVE,
);

// 6. Store the user, add to existing names, and update total amount of users
<RegisteredUsers<T>>::insert(&sender, user_metadata);
<Names<T>>::insert(&name_bounded, sender.clone());

// Note the use of 'unwrap_or_default' - this is better than just a plain 'unwrap()'
// The default for 'u32' is 0, meaning an 'unwrap_or(0)' could also work here!

let total_registered = <TotalRegistered<T>>::get().unwrap_or_default();

// The use of checked_add() ensures 'safe math' is taking place.
// Since we never want panic within a runtime, we have to ensure all *possible* errors
// can be caught.

<TotalRegistered<T>>::put(
total_registered.checked_add(1).ok_or(Error::<T>::IntegerOverflow)?,
);

// 7. Emit an event to indicate a new user was added to the network
Self::deposit_event(Event::Registered { id: sender });

Ok(())
}
- +

Creating dispatchable functions

With all of our tools in place to register a user, let's go through the process of adequately registering them via an extrinsicor state change.

What is an extrinsic again?

An extrinsic is a transaction or a unit to define how the state should change within the network.

Adding the register dispatchable

Navigate to the #[pallet::call] macro; it should be empty:

#[pallet::call]
impl<T: Config> Pallet<T> {
// Our dispatchable goes here.
}

We are going to create a function called register, which will take several parameters/factors into consideration and perform the following checks:

  • Take a parameter, name, of the user. The character amount must be below MaxNameLength.
  • Take a parameter, bio, of the user. The character amount must be below MaxBioLength.
  • Check if they have enough balance to lock, and if so, lock it. Else, they cannot register.
  • Generate a profile picture for our user
  • If the user meets the requirements, we store them in our StorageMap.
  • Emit an event that they registered.

Defining our sender and function

With our requirements adequately defined, we can begin coding this function. Go ahead and paste the function called register that includes some beginning logic to start:

#[pallet::call]
impl<T: Config> Pallet<T> {

/// Registers a user to the network. It requires the balance of the sender to have an amount
/// which is greater than or equal to MinimumLockableAmount. Locks MinimumLockableAmount as
/// part of the registration process.
#[pallet::call_index(0)]
pub fn register(origin: OriginFor<T>, name: Vec<u8>, bio: Vec<u8>) -> DispatchResult {
let sender = ensure_signed(origin)?;
}
}

Checking balance and using ensure! to check requirements

A very useful macro, ensure!, is provided by FRAME. This macro enables us to check a condition. If the condition proves false, it allows an extrinsic to fail with a specific error.

We can also use the Currency trait included with our configuration. We will elaborate more on this trait later but know that for now, it enables us to check the balance of the sender:

// Retrieve the "free" balance of the user
let balance = T::Currency::free_balance(&sender);

// Before proceeding - we have to make sure the *free* balance of a user is enough to
// lock up! Otherwise, we halt this dispatchable with an error.
ensure!(balance >= T::MinimumLockableAmount::get(), Error::<T>::LowBalance);

Unbounded to bounded

If you notice, the parameters (name and bio) provided in register are of type Vec<u8>. This is a vector of bytes, which you may now consider a String.

Within our config, we have two notable constants defined: MaxBioLength and MaxNameLength. We want our two parameters to be bounded to these limits, as we shouldn't allow for infinite values to be stored on the chain.

The following code does just that and maps the appropriate error if it does exceed this length. We use the type BoundedVec to convert from a Vec to something that is bounded:

let name_bounded: BoundedVec<u8, T::MaxNameLength> =
BoundedVec::try_from(name.clone()).map_err(|_| Error::<T>::NameTooLong)?;
let bio_bounded: BoundedVec<u8, T::MaxBioLength> =
BoundedVec::try_from(bio).map_err(|_| Error::<T>::BioTooLong)?;

// 2. Check if the name already exists or user metadata already exists
ensure!(<Names<T>>::get(&name_bounded).is_none(), Error::<T>::NameInUse);
ensure!(
<RegisteredUsers<T>>::get(&sender).is_none(),
Error::<T>::AccountIdAlreadyRegistered
);

Generate a gradient profile and build our user

We can build our user once we have our parameters converted and ready.

Firstly, we can call another trait, T::Randomness, to provide a random value to the included generate_hex_values function.

This will return two randomly generated hex values that can be used to create a gradient profile picture:

// Generate our random profile picture (aka, two hex values that form a gradient)
// Usually, some increasing nonce is used as a seed. For simplicity, we use the account
// id as the seed.
let (value, _) = T::Randomness::random(&sender.encode());
let random_pfp = Self::generate_hex_values(value);

// Construct our UserMetadata. Ideally, we could also create an implementation to make
// this easier to create!
let user_metadata: UserMetadata<T> = UserMetadata {
name: name_bounded.clone(),
bio: bio_bounded,
profile_gradient: random_pfp,
account_id: sender.clone(),
};

Lock balance and store our user

With our users fully configured, we can now lock their balance and finish the registration process by storing them in our RegisteredUsers mapping:

We also add the user's name to another mapping of names (Names) to ensure it doesn't get taken.

// Lock the minimum deposit.  This account will now have this amount locked until
// they 'de-register'.
T::Currency::set_lock(
LOCK_ID,
&sender,
T::MinimumLockableAmount::get(),
WithdrawReasons::RESERVE,
);

// Store the user, add to existing names, and update the total amount of users
<RegisteredUsers<T>>::insert(&sender, user_metadata);
<Names<T>>::insert(&name_bounded, sender.clone());

Update the total amount of users on the network

Once we register the user, we can update the total number of users on the network. Note the use of checked_add and unwrap_or_default():

// Note the use of 'unwrap_or_default' - this is better than just a plain 'unwrap()'
// The default for 'u32' is 0, meaning an 'unwrap_or(0)' could also work here!

let total_registered = <TotalRegistered<T>>::get().unwrap_or_default();

// Using checked_add() ensures 'safe math' occurs.
// Since we never want panic within a runtime, we have to ensure all *possible* errors
// can be caught.

<TotalRegistered<T>>::put(
total_registered.checked_add(1).ok_or(Error::<T>::IntegerOverflow)?,
);

Emit an event

Lastly, we can emit an event once everything above is performed to indicate a new user has been registered:

// Emit an event to indicate a new user was added to the network
Self::deposit_event(Event::Registered { id: sender });

Full register function

The entire register function should end up looking like this by the end:

    #[pallet::call_index(0)]
pub fn register(origin: OriginFor<T>, name: Vec<u8>, bio: Vec<u8>) -> DispatchResult {
let sender = ensure_signed(origin)?;
let balance = T::Currency::free_balance(&sender);

// Before proceeding - we have to make sure the *free* balance of a user is enough to
// lock up! Otherwise, we halt this dispatchable with an error.
ensure!(balance >= T::MinimumLockableAmount::get(), Error::<T>::LowBalance);

// 1. Craft the user metadata out of the given parameters from `register`.
// Keep in mind we have to cast these to `BoundedVec` using the limits we have defined
// in our Config (hence why we must access them using our handy `T` generic operator!).
// Notice the error handling! Other types of error handling are okay too :)

let name_bounded: BoundedVec<u8, T::MaxNameLength> =
BoundedVec::try_from(name.clone()).map_err(|_| Error::<T>::NameTooLong)?;
let bio_bounded: BoundedVec<u8, T::MaxBioLength> =
BoundedVec::try_from(bio).map_err(|_| Error::<T>::BioTooLong)?;

// 2. Check if the name already exists or user metadata already exists
ensure!(<Names<T>>::get(&name_bounded).is_none(), Error::<T>::NameInUse);
ensure!(
<RegisteredUsers<T>>::get(&sender).is_none(),
Error::<T>::AccountIdAlreadyRegistered
);

// 3. Generate our random profile picture (aka, two hex values which form a gradient)
// Usually, some increasing nonce is used as a seed. For simplicity, we use the account
// id as the seed.
let (value, _) = T::Randomness::random(&sender.encode());
let random_pfp = Self::generate_hex_values(value);

// 4. Construct our UserMetadata. Ideally, we could also create an implementation to make
// this easier to create!
let user_metadata: UserMetadata<T> = UserMetadata {
name: name_bounded.clone(),
bio: bio_bounded,
profile_gradient: random_pfp,
account_id: sender.clone(),
};

// 5. Lock the minimum deposit. This account will now have this amount locked until
// they 'de-register'.
T::Currency::set_lock(
LOCK_ID,
&sender,
T::MinimumLockableAmount::get(),
WithdrawReasons::RESERVE,
);

// 6. Store the user, add to existing names, and update total amount of users
<RegisteredUsers<T>>::insert(&sender, user_metadata);
<Names<T>>::insert(&name_bounded, sender.clone());

// Note the use of 'unwrap_or_default' - this is better than just a plain 'unwrap()'
// The default for 'u32' is 0, meaning an 'unwrap_or(0)' could also work here!

let total_registered = <TotalRegistered<T>>::get().unwrap_or_default();

// The use of checked_add() ensures 'safe math' is taking place.
// Since we never want panic within a runtime, we have to ensure all *possible* errors
// can be caught.

<TotalRegistered<T>>::put(
total_registered.checked_add(1).ok_or(Error::<T>::IntegerOverflow)?,
);

// 7. Emit an event to indicate a new user was added to the network
Self::deposit_event(Event::Registered { id: sender });

Ok(())
}
+ \ No newline at end of file diff --git a/docs/Substrate/section5/unit-tests.html b/docs/Substrate/section5/unit-tests.html index 61820af11..e12f567f0 100644 --- a/docs/Substrate/section5/unit-tests.html +++ b/docs/Substrate/section5/unit-tests.html @@ -5,13 +5,13 @@ Writing unit tests for pallets | Polkadot Education Initiative - +

Writing unit tests for pallets

With our dispatchable function written, it's time to ensure we understand how to unit test it. Substrate and FRAME provide tools for unit tests without spinning up a local chain, including a mock environment and traditionally written unit tests.

Overview of mock.rs

The purpose of mock.rs is to mock a runtime configuration. Upon observing it, you can notice that most types are regular, primitive types that are in place of the more abstract types inside of runtime/lib.rs:

impl frame_system::Config for Test {
...
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
...
}

For example, AccountId is u64, meaning a valid account address can be an unsigned integer, i.e.,

let bob_account: u64 = 1;

The simplification of types is for simplifying the testing process.

Test Externalities

To configure and produce an environment, notice the function at the bottom of mock.rs:

// Build genesis storage according to the mock runtime.
pub fn new_test_ext() -> sp_io::TestExternalities {
frame_system::GenesisConfig::default().build_storage::<Test>().unwrap().into()
}

This function defines a 'testbed' for your tests to simulate aspects like the storage layer.

Writing a unit test for register

info

Navigate to pallets/connect/src/tests.rs

Unit tests are placed within tests.rs. They utilize the mock environment and configuration to test various scenarios with the pallet. These are constructed like traditional Rust unit tests, with the caveat that these tests must be run within the TestExternalities instance defined in mock.rs.

Using unit tests, we can test extrinsics, storage, errors, and events using our mock environment.

Unit Test Cheatsheet

For testing, there are numerous macros and APIs one could call:

  • assert!, assert_ok!, and assert_eq! work as per normal unit testing in Rust.
  • To check if an event has been emitted, use System::assert_last_event() with the Event as the parameter. You may need to use .into().
  • To check if an error has been emitted, use assert_noop!(call, Error::<Test>::SomeError);. Notice the ::<Test>:: turbofish syntax used to call the error with the Test config as the generic parameter.
  • If events aren't seemingly emitted within tests, be sure to set the testing environment to a height of at least one: System::set_block_number(1);

Low balance test example

While all the tests are in tests.rs, here is an example of a unit test. FRAME provides numerous helper APIs, such as System, to perform actions like setting the block height for the test environment. See it in action below:

#[test]
fn balance_too_low() {
new_test_ext().execute_with(|| {
let name = b"polkadot".to_vec();
let bio = b"A heterogeneous, sharded network.".to_vec();
// Go past genesis block so events get deposited
System::set_block_number(1);
// Set the balance to 9 DOT - too low
assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1, 9));
// Dispatch a signed extrinsic.
assert_noop!(
Connect::register(RuntimeOrigin::signed(1), name.clone(), bio),
Error::<Test>::LowBalance
);
});
}
- + \ No newline at end of file diff --git a/docs/Substrate/section6.html b/docs/Substrate/section6.html index c96bd4bc9..7ea805908 100644 --- a/docs/Substrate/section6.html +++ b/docs/Substrate/section6.html @@ -5,13 +5,13 @@ FRAME Pallet Tests and Deployment | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Substrate/section6/run-node.html b/docs/Substrate/section6/run-node.html index 8ff79d902..2ff705b61 100644 --- a/docs/Substrate/section6/run-node.html +++ b/docs/Substrate/section6/run-node.html @@ -5,13 +5,13 @@ Running the node template | Polkadot Education Initiative - +

Running the node template

caution

We are now working in the node's root directory (substrate-mooc-node), not the pallet directory.

With all of our functionality in place, it is finally time to run and view our node running in an actual environment.

Running tests

Before running, it is a good idea to run tests to make sure everything is working as it should:

cargo test

Launching the node

If the tests pass, you may now launch the node. Ensure you are in the root directory of the node:

info

Note that we are running in dev mode, meaning our node has finalization without needing another node to run.

cargo run -- --dev

This may take some time to build and run. Once it does, you should see the following output:

2023-06-21 17:13:03 Substrate Node
2023-06-21 17:13:03 ✌️ version 4.0.0-dev-6187dc90dff
2023-06-21 17:13:03 ❤️ by Substrate DevHub <https://github.com/substrate-developer-hub>, 2017-2023
2023-06-21 17:13:03 📋 Chain specification: Development
2023-06-21 17:13:03 🏷 Node name: brief-fiction-9945
2023-06-21 17:13:03 👤 Role: AUTHORITY
2023-06-21 17:13:03 💾 Database: RocksDb at /var/folders/x0/hd8wpwmx2kgfqv8m03wxj2mw0000gn/T/substrateSTe3Ji/chains/dev/db/full
2023-06-21 17:13:03 ⛓ Native runtime: node-template-100 (node-template-1.tx1.au1)
2023-06-21 17:13:07 🔨 Initializing Genesis block/state (state: 0x8fc4…d28d, header-hash: 0x0e62…f7d0)
2023-06-21 17:13:07 👴 Loading GRANDPA authority set from genesis on what appears to be first startup.
2023-06-21 17:13:09 Using default protocol ID "sup" because none is configured in the chain specs
2023-06-21 17:13:09 🏷 Local node identity is: 12D3KooWDTSe4xUvaa4pjVXAZKFsm2LBTUaZQ7ZVdDgvBA4bJtQR
2023-06-21 17:13:09 💻 Operating system: macos
2023-06-21 17:13:09 💻 CPU architecture: aarch64
2023-06-21 17:13:09 📦 Highest known block at #0
2023-06-21 17:13:09 〽️ Prometheus exporter started at 127.0.0.1:9615
2023-06-21 17:13:09 Running JSON-RPC HTTP server: addr=127.0.0.1:9933, allowed origins=["*"]
2023-06-21 17:13:09 Running JSON-RPC WS server: addr=127.0.0.1:9944, allowed origins=["*"]
2023-06-21 17:13:11 Accepting new connection 1/100
2023-06-21 17:13:12 🙌 Starting consensus session on top of parent 0x0e6244a5f0b8da2a2a424fcafcdbc4e11ab4574e1e016a403d7809e67b42f7d0
2023-06-21 17:13:12 🎁 Prepared block for proposing at 1 (1 ms) [hash: 0xdcbeb0870914fc440922d38e3ba7b3f2f7a20e176f99eeda6665c1dd6db97132; parent_hash: 0x0e62…f7d0; extrinsics (1): [0xcae8…b246]]
2023-06-21 17:13:12 🔖 Pre-sealed block for proposal at 1. Hash now 0x8010a14bfe679f7efaeaa966289340ddabec13318a7a60eb1e1be4d64110a834, previously 0xdcbeb0870914fc440922d38e3ba7b3f2f7a20e176f99eeda6665c1dd6db97132.
2023-06-21 17:13:12 ✨ Imported #1 (0x8010…a834)

If you get this output, congratulations! You can see your network producing blocks, as stated by 🎁 Prepared block for proposing at 1. With your node running, you can now use either the substrate-frontend-template or the Polkadot.js app to interact with your node and view network stats.

- + \ No newline at end of file diff --git a/docs/Substrate/section6/test-frontend.html b/docs/Substrate/section6/test-frontend.html index adfaa2092..0eaec6373 100644 --- a/docs/Substrate/section6/test-frontend.html +++ b/docs/Substrate/section6/test-frontend.html @@ -5,13 +5,13 @@ Use the frontend-template to test your pallet | Polkadot Education Initiative - +

Use the frontend-template to test your pallet

With your node running, launching the frontend template from the previous module would be a good idea. The frontend template will allow you to interact with the node on a fundamental level while also accessing different functionality through the various pallets, including our custom connect pallet.'

Launch the frontend template

Make sure your node and the frontend template are running:

cd substrate-frontend-template/
yarn start

Quick tour

Located at http://localhost:8000/substrate-frontend-template, you should be greeted with this page (as seen previously). It shows various network statistics regarding your node:

Frontend Overview

Frontend Network

Using this template, we can also interact with our node using the pallet interactor. Be sure to have the ALICE account selected, as it is pre-funded for sending extrinsics:

frontend accounts

Registering a user with the Pallet Interactor

The bottom half of the frontend template features the ability to perform forkless upgrades, the pallet interactor, and an event viewer:

Frontend Pallets

We will use the pallet interactor to register a new user to the network. Select the first drop-down, select our pallet connect, then our register extrinsic in the second drop-down. You'll notice two parameters: the name and bio, which are filled accordingly with text:

Frontend Interactor

Once all parameters are filled, select Signed. This will send the transaction, or extrinsic, to be confirmed in a block within our node.

Notice that the event view immediately emits several events indicating that our registration was successful. The user has been registered, and the balance is locked and stored within the state.

Frontend Events

Later, you should also see this below the pallet interactor, indicating that the network has finalized the state change:

Frontend Finalized

What is signed, unsigned, and SUDO?

These three buttons represent different origins for the extrinsic. If you remember, we used the macro ensure_signed within our pallet to indicate that we expect a signed extrinsic.

Querying state

To query state, make sure to select "Query" as the interaction type:

Frontend Query Type

As before, select the connect pallet, enter the name "Alice" to query the user metadata for Alice, and hit "Query":

frontend Query Registered Users

Congratulations, our custom pallet works!

- + \ No newline at end of file diff --git a/docs/Substrate/section6/use-polkadotjs.html b/docs/Substrate/section6/use-polkadotjs.html index cdd98c7eb..827a1e04a 100644 --- a/docs/Substrate/section6/use-polkadotjs.html +++ b/docs/Substrate/section6/use-polkadotjs.html @@ -5,14 +5,14 @@ Using Polkadot.js to explore your storage | Polkadot Education Initiative - +

Using Polkadot.js to explore your storage

Although the substrate-frontend-template is convenient, in most development scenarios, you would use Polkadot.js.

With your node still running, navigate to the Polkadot.js Localhost:

Polkadot JS Overview

This is a more real-time experience compared to the frontend template. Note the events viewer on the right, which will soon be populated!

Getting detailed storage

Navigate to the Developer > Chain state tab, and select the connect pallet from the dropdown. Just as before, we can query the "Alice" name we registered before:

Polkadot JS State Query

Sending an extrinsic and viewing events: Polkadot.js edition

Navigate to the Developer > Extrinsics tab, and select the BOB account from the list of accounts. s Select the connect pallet from the dropdown, and the register extrinsic from the dropdown to the right:

Polkadot JS Extrinsic

Fill out the details for the parameters in their respective fields, and press Submit transaction in the bottom right. Once you finish this, you should see two events emit in the Network > Explorer tab:

Polkadot JS Events

- + \ No newline at end of file diff --git a/docs/Substrate/section7.html b/docs/Substrate/section7.html index dc43461c3..b979f23c6 100644 --- a/docs/Substrate/section7.html +++ b/docs/Substrate/section7.html @@ -5,13 +5,13 @@ FRAME - Best Practices | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Substrate/section7/blockchain-dev.html b/docs/Substrate/section7/blockchain-dev.html index 4a6251ab6..d0ce304b8 100644 --- a/docs/Substrate/section7/blockchain-dev.html +++ b/docs/Substrate/section7/blockchain-dev.html @@ -5,13 +5,13 @@ Thinking in terms of blockchain development | Polkadot Education Initiative - +

Thinking in terms of blockchain development

If you have come from a web2 background, it can be a learning curve to "think like a blockchain developer".

While this course merely touches on the more surface-level capabilities of Substrate, there are some critical practices and concepts to remember when starting your journey to develop blockchains with Substrate.

On Cryptography - "Don't roll your own crypto"

It is important to abide by the general rule of thumb of "not rolling your own crypto". Use established or community-approved algorithms.

Whether it is apparent or not, all blockchain-based systems rely heavily on various cryptographic methods to track, verify, and provide the integrity blockchains intrisically offer.

As we've seen with the StorageMap implementation, we used the Blake2 hashing algorithm to hash our storage keys, or more important; we require an extrinsic to be a cryptographically valid, signed payload that represents that specific call. The blockchain itself relies on a series of hashes that build on each other to form the infamous, verifiable ledger that is the blockchain.

Not using approved cryptography within your chain could make it fundamentally insecure and vulnerable to attack.

On Storage - Blockchains aren't just databases

There is a common misconception that a blockchain is simply a distributed database; with that thinking, any data can be put on the blockchain. When developing a pallet or dApp, it is important to realize do not store data, but rather a representation of it.

A real-world example: Video on blockchain

In this hypothetical scenario, a platform wishes to gate access to a video streaming platform unless certain NFTs are owned. Video is a computationally expensive to store and manage - so would the blockchain instance store each video?

The answer is no. In most cases, the blockchain merely points to the content but doesn't store it on the network.

As we've seen, blockchains are not simply just a database - they represent an autonomous way to agree upon the state of some network. In order to agree on this state of the network, each node must hold some semblance of a copy of the network. In many cases, full nodes bear the brunt of the load when it comes to proving the state and providing access to it. Moreover, storing data on-chain is "expensive". While fees aren't an issue in our developmental network, a network like Polkadot requires a fee for a state change. The more data in that state change, the more computation and storage it will cost the network.

- + \ No newline at end of file diff --git a/docs/Substrate/section7/how-to-test-frame.html b/docs/Substrate/section7/how-to-test-frame.html index e8a763f0c..2bc1755b5 100644 --- a/docs/Substrate/section7/how-to-test-frame.html +++ b/docs/Substrate/section7/how-to-test-frame.html @@ -5,13 +5,13 @@ How to approach testing in FRAME | Polkadot Education Initiative - +

How to approach testing in FRAME

In Module 5, we briefly reviewed a basic unit test implementation for our pallet. This section will provide more depth to testing a pallet in Substrate and how to approach it in general.

Good Practices - Test Everything

As discussed in Runtime Panics & FRAME Best Practices, your pallet should never explicitly panic due to something like unwrap()-ing an invalid value or even accounting for edge cases like integer overflows by using safe arithmetic.

The same approach of ensuring any edge case is covered in the runtime can be reinforced in unit tests. Ideally, every error should have an accompanying (or inclusive) unit test to ensure the error is correctly handled.

Mocking Types and Pallets

Unit tests require a mock runtime environment to be defined. Because using actual primitives could be prohibitive in testing, types are mostly mocked.

If you go back to the substrate-mooc-node/pallets/connect/src/mock.rs and observe the mock runtime called Test, AccountId is a prime example of a simple, mock primitive as u64:

impl frame_system::Config for Test {
...
type AccountId = u64;
...
}

An account id is simply a number versus a more complex type in testing. You may also notice many types in this configuration are merely just the Rust unit type, as they aren't relevant in the context of this mock environment (at least for our specific pallet) as denoted by SomeConfigParam = ().

Other types, such as a Block within the runtime, are helpfully defined via frame_system::mocking::MockBlock<Test>.

Mocked Pallets

Because pallets are external modules that expose several traits and their respective struct, they can also be configured via these mock types. Take a look at pallet_balance, which is defined within mock.rs:

impl pallet_balances::Config for Test {
type Balance = u128;
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type ReserveIdentifier = [u8; 8];
}

Again, notice how Balance is defined as a primitive Rust u128 type.

Case Study: Randomness

For some other traits/pallets, it's possible that frame_support_test can provide extra mock and testing-related crates. For example, TestRandomness<T> is a mock type for randomness since we don't have a running chain to generate the traditional entropy. For this reason, it also makes our tests much more predictable (because it's a predictable source of randomness for testing), which is ideal:

/// These values will always be the same
#[test]
fn generate_gradient_with_correct_length() {
let hex = Connect::generate_hex_values(H256([0; 32]));
println!("{:?}", hex);
assert_eq!(hex.0, [8, 48, 48]);
assert_eq!(hex.1, [8, 48, 48]);
}

It's also possible to define your types for this substitution.

- + \ No newline at end of file diff --git a/docs/Substrate/section7/runtime-panics.html b/docs/Substrate/section7/runtime-panics.html index c19819bf0..bee4e917d 100644 --- a/docs/Substrate/section7/runtime-panics.html +++ b/docs/Substrate/section7/runtime-panics.html @@ -5,13 +5,13 @@ Runtime Panics & FRAME Best Practices | Polkadot Education Initiative - +

Runtime Panics & FRAME Best Practices

If there is one "rule of thumb" in developing in a runtime environment, is to avoid runtime panics at all costs. With the premise that a blockchain should never cease to operate, abusing the notion of panic!(king) in Rust should never be used.

Avoiding runtime panics - Defensive Programming

Avoiding runtime panics is done by implementing some concepts of defensive programming. Here is a checklist of some common things to avoid and do in order to avoid panicking:

  1. Account for any and all edge cases in terms of what could panic.
  2. Do not use panic!().
  3. Do not use unwrap().
  4. Handle all possible None or Err values with proper error handling.
  5. When indexing collections, use Vec methods like get() and handle appropriately.
  6. Mathematically impossible operations, such as dividing by zero, or overflow scenarios.

In some cases, using unwrap_or_default() is appropriate, but only if a Default implementation exists for that particular type. Generally, you do not want to throw errors, rather you want to log and return them.

Logging has a cost in the runtime.

Keep in mind that logging has a computational cost in the runtime environment.

Using safe arithmetic

In the previous modules, we used a function - checked_add(). This is part of defensive programming, as we even negate the chance of an overflow of an integer. Substrate provides a number of ways to handle not only adding or subtracting numbers, but also floating point numbers (and subsequently, percentages).

Accounting for all errors

As mentioned, every single error should be accounted for - even if logically, that error cannot be reached. This is part of ensuring that any functions that can panic unwrap(), have even a chance to panic. Whether it's for casting to a different type, user-induced errors, or the like - all branched paths should be covered.

- + \ No newline at end of file diff --git a/docs/Substrate/section8.html b/docs/Substrate/section8.html index b7b31b606..314306c89 100644 --- a/docs/Substrate/section8.html +++ b/docs/Substrate/section8.html @@ -5,13 +5,13 @@ FRAME Deepdive | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/Substrate/section8/benchmarking.html b/docs/Substrate/section8/benchmarking.html index 182a3776d..1f0f09cba 100644 --- a/docs/Substrate/section8/benchmarking.html +++ b/docs/Substrate/section8/benchmarking.html @@ -5,13 +5,13 @@ Introduction to Benchmarking | Polkadot Education Initiative - +

Introduction to Benchmarking

This section will touch briefly on the concept weights and benchmarking in Substrate. While this section will not go in-depth, any Substrate developer should know it when developing a pallet. In the long run, producing production-ready pallets will become crucial.

Weights in Substrate

Until now, fees have not been a concern for our Connect pallet because we are in dev_mode. However, fees are a reality within the realm of pallet development.

Weight in Substrate refers to the overall computational, storage, and execution cost of a particular extrinsic (state change). Weight is the measurement of the amount of time that a state change takes to execute fully. One unit of weight is equal to one picosecond of execution time on reference hardware.

Based on this weight, a fee is charged to the sender of the extrinsic.

An Introduction to Benchmarking

To calculate an appropriate weight for an extrinsic, Substrate's benchmarking suite can test each extrinsic load to estimate how much weight each state change would take. A weights.rs file is generated via the included CLI and exposes a set of pre-defined weights to use as part of the dispatchable function declaration.

Like unit testing, each extrinsic should have a corresponding benchmarking function within benchmarking.rs.

note

As a general note, we are using Benchmarking V2 within this course, which uses procedural macros to declare benchmarking tests.

Example: Baseline Benchmark for register

The following test is a simple benchmark that uses several features. Usually, the idea is to benchmark each logical execution path for your extrinsic. When calling the extrinsic, we utilize the #[extrinsic_call] macro within our benchmarking function.

Because our function is quite simple, we only have a single baseline benchmark for our function, register:

#[benchmark]
fn register_an_account() {
let caller: T::AccountId = whitelisted_caller();

T::Currency::make_free_balance_be(&caller, 10u32.into());

let very_long_name = b"123456789".to_vec();
let very_long_bio = b"USOsEy3cAmZudmWyUEMdlU6wVXsZeMj7Ts8rh7Laur3L1ZpvvorGOcZw17mDGtNhmxqYRnANsOxhhfauuRxJhz1PRtHKoXai0i3lT0cTFqpCGODLvRxk8MOiMmVMdoylxwXYMVMwoYuZJQStM9t8k4m9aESUQ5rcCkH408t9s4Yz3WfyvbZfF5bROFgrHug9uk4Iar7Q".to_vec();

#[extrinsic_call]
register(RawOrigin::Signed(caller.clone()), very_long_name, very_long_bio);
assert!(<RegisteredUsers<T>>::get(&caller).is_some());
}
- + \ No newline at end of file diff --git a/docs/Substrate/section8/chain-genesis-spec.html b/docs/Substrate/section8/chain-genesis-spec.html index d6265afca..f5c5a920c 100644 --- a/docs/Substrate/section8/chain-genesis-spec.html +++ b/docs/Substrate/section8/chain-genesis-spec.html @@ -5,13 +5,13 @@ Chain Specification | Polkadot Education Initiative - +

Chain Specification

The chain specification describes any Substrate-based network. Located in node/src/chain_spec.rs, the chain specification describes the initial state of the network via the ChainSpec struct, which is usually exported as a JSON file using the substrate build-spec command.

When we start our chain in developmental mode, we have a set of pre-defined, funded accounts (endowed_accounts). These accounts are inside chain_spec.rs where default accounts, the chain's name, id, sudo accounts, and more are defined.

chain_spec.rs Overview

The chain_spec.rs file describes a Substrate-based chain. It includes numerous functions, each defining a particular aspect of the configuration. The most notable functions are:

  • fn testnet_genesis() -> Defines the initial pallet state, including the Wasm runtime.
  • fn development_config() -> Defines the developmental instance of the node. Uses testnet_genesis() to configure the state of the development state.
Why does my chain progress in development mode with only one node?

If you have run cargo run --release on its own, and the chain doesn't progress, that's because it requires at least two authorities to start authoring blocks. The developmental configuration only requires a single validator, Alice.

Modifying Your Chain Spec

It is possible to modify these functions to define your chain's initial state. New authorities for validation could be added, fund other accounts other than the "well-known" accounts, and any registered pallets' initial state can be modified in general.

Example: Changing the name of your chain

Navigate to development_config() and find the following lines:

pub fn development_config() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;

Ok(ChainSpec::from_genesis(
// Name
"Development",
...

Go ahead and change the name to whatever you like. By the end, it should look like this:

pub fn development_config() -> Result<ChainSpec, String> {
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;

Ok(ChainSpec::from_genesis(
// Name
"TechEd Development Chain",
...

If you run your node and navigate to Polkadot.js, it should show in the upper left:

Development Chain Name PolkadotJs

- + \ No newline at end of file diff --git a/docs/Substrate/section8/origins-calls.html b/docs/Substrate/section8/origins-calls.html index 791f893c2..df584a2b4 100644 --- a/docs/Substrate/section8/origins-calls.html +++ b/docs/Substrate/section8/origins-calls.html @@ -5,13 +5,13 @@ Origins (Privileged Calls) in FRAME | Polkadot Education Initiative - +

Origins (Privileged Calls) in FRAME

In Module 5, you created a dispatchable function called register that defined an extrinsic. The first parameter in that function, OriginFor<T>, enabled you also to interpret the call's sender, or origin. There are different types of origins, which can be used in various ways to filter out where a call should come from.

Origin Overview

A runtime origin defines where a call came from and can be used to check its type. In our dispatchable code, we used the macro ensure_signed!() to verify that whoever sent this transaction signed the payload. Without this check in place, this would mean that anyone could submit a payload, regardless of whether it is cryptographically signed or not.

This allows us to authenticate the user and gain access to necessary metadata, such as the account address.

Origin Types (Raw Origins)

The above is known as a signed origin, and several types are included by default in Substrate (see: RawOrigin):

  • Root - Similar to using "sudo", known as the highest privilege level. The root origin is utilized in scenarios like upgrading the runtime.
  • Signed - Some payload is signed by a private/public key pair and provides an AccountId.
  • None - Signed by nobody. Unsigned transactions are rarely used, except in cases like off-chain workers or where charging an account with fees is unreasonable.

Custom Origins

Besides the default RawOrigin types, custom origins can be defined and used within a pallet. Custom origins allow a developer to define a stringent set of requirements for what a call should contain to be valid for that specific use case.

For example, the pallet_collectives defines custom origins that represent a Member(AccountId) of a particular collective. In the pallet's context, a call containing this origin would imply that a member of that collective condoned that action.

- + \ No newline at end of file diff --git a/docs/Substrate/section8/pallet-coupling.html b/docs/Substrate/section8/pallet-coupling.html index 0e3a1faa3..1af3b8cd3 100644 --- a/docs/Substrate/section8/pallet-coupling.html +++ b/docs/Substrate/section8/pallet-coupling.html @@ -5,13 +5,13 @@ Pallet Coupling | Polkadot Education Initiative - +

Pallet Coupling

Pallets can use each other as dependencies to access their specific functionalities. For example, we use pallet_balance to access our pallet's underlying balances and lock statuses.

This is referred to as pallet coupling, and they define how pallets can interact with each other on a software level. This is similar to object-oriented programming, where tight and loose coupling indicates the relationship between two data structures. In Rust, this refers to how various traits and associated types are used with one another so that they may call upon each other's respective methods and implementations.

Loose Coupling

Our pallet used loose coupling, as we merely utilized an exposed trait from the Balances pallet (Currency). We further stated that we wish for our notion of Currency also to contain the traits ReservableCurrency and LockableCurrency, which further opens up our configuration to potentially other pallets:

// Loosely including Currency in the configuration
/// Configure the pallet by specifying the parameters and types it depends on.
#[pallet::config]
pub trait Config: frame_system::Config {
...
type Currency: ReservableCurrency<Self::AccountId>
+ LockableCurrency<Self::AccountId, Moment = Self::BlockNumber>;
...
}

Tight Coupling

Tight coupling a pallet involves explicitly declaring an entire, specific pallet as a dependency. This would also mean that this pallet has to be within your pallet's Cargo.toml file, precisely just that specific pallet's instance. An example of this is the Bounties pallet, which tightly couples the Treasury pallet to it:

/// Notice the use of the '+' operator to tightly couple the Config of pallet_treasury
pub trait Config<I: 'static = ()>: frame_system::Config + pallet_treasury::Config<I> {}

Tightly coupling two pallets in this manner is helpful when they share very similar goals and objectives and for pallets that are simple enough. Because the Bounties pallet solely depends on the Treasury pallet for payouts (and its subsequent types), it is sensible to couple it tightly.

Which to use?

Overall, for maintainability and flexibility, loosely coupling is preferred. The benefit of loosely coupling our pallet was to reduce the apparent need to bring in the entire pallet versus just utilizing the traits you need.

- + \ No newline at end of file diff --git a/docs/intro.html b/docs/intro.html index fa56cabe4..1a5648fd1 100644 --- a/docs/intro.html +++ b/docs/intro.html @@ -5,13 +5,13 @@ Web3 Education Initiative | Polkadot Education Initiative - +

Web3 Education Initiative

Four courses - Blockchain Fundamentals, Introduction to Polkadot, Introduction to Rust Programming and Introduction to Substrate

- + \ No newline at end of file diff --git a/docs/introatoz.html b/docs/introatoz.html index 056db55c0..7ba91a461 100644 --- a/docs/introatoz.html +++ b/docs/introatoz.html @@ -5,13 +5,13 @@ W3F A - Z ELI5 series | Polkadot Education Initiative - + - + \ No newline at end of file diff --git a/docs/introblock.html b/docs/introblock.html index 74ad41126..6e7e0e7ba 100644 --- a/docs/introblock.html +++ b/docs/introblock.html @@ -5,7 +5,7 @@ Blockchain Basics | Polkadot Education Initiative - + @@ -15,7 +15,7 @@ Introduction to Bitcoin. Innovations in Blockchain Technology. Cryptocurrencies, Web3 and more

Module 2 - Blockchain and Cryptography

Cryptography in Blockchain. Public/Private keypairs and how do wallets work? What is a Hash? Properties of Cryptographic Hashing. Is Blockchain invincible? Blockchain data structures. Unforgeability game and the role of a central authority. Decentralization and the Trilemma.

Module 3 - Block Mining and Finality

How Bitcoin blocks are mined? Anatomy of a Bitcoin Block. What is Forking? Major Bitcoin forks. What is Hashpower? How can miners tamper with PoW? Energy implications of PoW. What are the other ways to mine/validate a Block? PoS. What are other ways to finalize a Block?

Module 4 - Blockchain Nodes and Networking

Distributed systems. Can I run my own Blockchain node? What are minimum hardware requirements to run blockchain nodes out there? How do Blockchain nodes communicate? Interfacing with Blockchain network. API. Full node vs Archive node vs light client. Consensus in depth. 51% attacks.

Module 5 - Blockchain Layers and Applications

What problems can Blockchain solve? What is cheaper and reliable to do on Blockchain? Does your application need to be on a Blockchain? L0, L1 and L2 Blockchains. Lightning networks. EVM and Smart contracts. Crosschain swaps. Anonymity and confidential transactions. zkledger. zkrollups. Blockchain for enterprise.

Module 6 - Web3 and the Future of Blockchain

Scams and Attacks. Culture. Public policy. Regulations. Blockchain governance. Cryptocurrencies and DeFi. DAOs. Interoperability and Scalability. NFTs. Metaverse. Decentralized computing.

- + \ No newline at end of file diff --git a/docs/introdot.html b/docs/introdot.html index 6d8b0d3f8..7232b1dde 100644 --- a/docs/introdot.html +++ b/docs/introdot.html @@ -5,13 +5,13 @@ Introduction to Polkadot | Polkadot Education Initiative - +

Introduction to Polkadot

Developed by the Technical Education team at the Web3 Foundation, this course provides a comprehensive overview of Polkadot and blockchain research at the Web3 Foundation. The course starts with an introductory module exploring the question “What is Polkadot?”. We then move to interacting with the Polkadot network and key terminology to be aware of. Later, we dive deeper into the concepts of the shared security, interoperability, cryptography and networking aspects of Polkadot. We wrap up the course by taking a look at the development happening on the Polkadot ecosystem and how to get involved.

Module 1 - What is Polkadot?

Introduction to Polkadot from the founders, vision of Polkadot and an overview of Polkadot network architecture - relay chain, parachains and network protocol. Introduction to the concepts of scalability, interoperability and security. Introduction to Kusama. Testnets vs canary networks. Overview of on-chain governance. Applications and benefits of Polkadot. Composability of L1 blockchains.

Module 2 - Interacting with Polkadot

DOT as a network utility token. Creating and restoring accounts, existential deposit, balance transfers, wallets, staking and validating, participating in governance, setting on-chain identity, remarks, parachain auctions, crowdloans and avoiding scams. Block explorers. Proxies. API.

Module 3 - Consensus and Security

Hybrid Consensus: BABE and GRANDPA with an introduction to SASSAFRAS. Introduction to VRF. Securing the network using Nominated Proof of Staking. Validator roles explored in depth. Staking rewards and inflation models. Comparison with other networks. Explain the current limitations and planned improvements. PoA to NPoS process.

Module 4 - Cryptography and Networking

Account keys and session keys. Overview of networking - gossiping, distributed service, storage and availability, authentication, transport and discovery. Polkadot telemetry. Challenges and scope for improvements.

Module 5 - Interoperability and Scalability

Polkadot interoperability protocol in depth. Relay chain vs parachain. Auction process and parachain slots. Path of a parachain block. Validity and availability. Cross chain messaging (XCM). Introduction to parathreads and common good parachains. Statemint. Teleporting assets.

Module 6 - Polkadot for Developers

Introduction to Rust. Introduction to Substrate and FRAME. Polkadot-JS and APIs. SPREE. Wasm and Polkadot as a Wasm meta-protocol . Polkadot ecosystem and developers social channels. SmolDOT - blockchain light clients. Substrate recipes and Playground. Westend and Rococo. Canvas INK smart contracts. Future of development on Polkadot

- + \ No newline at end of file diff --git a/docs/introparachain.html b/docs/introparachain.html index e237bf9ad..a5c7a8437 100644 --- a/docs/introparachain.html +++ b/docs/introparachain.html @@ -5,13 +5,13 @@ Parachain Development Guide | Polkadot Education Initiative - +

Parachain Development Guide

This guide aims to jumpstart a developer from an overview of Polkadot architecture, creating their own parachain, and going through the process of registering it to a locally available relay chain.

  • Learn the basics of how Polkadot works as a network.
  • Learn the roles of the relay and parachain architecture.
  • Install necessary binaries and dependencies needed to create networks locally.
  • Modify your parachain and prep it for connecting to the relay chain.
  • Create a parathread and reserve a para ID
  • Create and run an auction, registering your parachain.
  • Next steps and moving forward.
- + \ No newline at end of file diff --git a/docs/introrust.html b/docs/introrust.html index aa3fd18f2..c3ed89850 100644 --- a/docs/introrust.html +++ b/docs/introrust.html @@ -5,13 +5,13 @@ Introduction to Rust for Blockchain Development | Polkadot Education Initiative - +

Introduction to Rust for Blockchain Development

Developed by the Technical Education team at the Web3 Foundation, this course introduces programming in Rust for Blockchain applications.

This course follows the free textbook The Rust Programming Language by Steve Klabnik and Carol Nichols, with the addition of hands-on examples, in-browser executable snippets, and some patterns commonly found in blockchain-based development.

This course is designed to teach the fundamentals of Rust to people who know the basics of programming in another language, and are curious about why Rust is used and how to use it.

This course provides a strong foundation in general-purpose Rust. Also, it emphasizes some aspects of Rust (such as [no_std], advanced traits, and the use of Rust & Wasm development toolchain) that are particularly useful when developing with the Substrate blockchain framework.

Module 1 - Why Learn Rust?

Module 2 - Rust 101 - The Basics

Module 3 - Intro to Intermediate Rust: Ownership, Borrowing, & Slices

Module 4 - Intro to Intermediate Rust: Enums & Matching Patterns

Module 5 - Intro to Intermediate Rust - Data Structs & Collections

Module 6 - Intro to Advanced Rust - Traits, Generics, & Lifetimes

Module 7 - Intro to Advanced Rust - Iterators & Closures

Module 8 - Learning Cargo, Rust’s Package Management System & Unit Testing

- + \ No newline at end of file diff --git a/docs/introsubstrate.html b/docs/introsubstrate.html index e81360b9c..439bf378b 100644 --- a/docs/introsubstrate.html +++ b/docs/introsubstrate.html @@ -5,13 +5,13 @@ Introduction to Substrate & FRAME | Polkadot Education Initiative - +

Introduction to Substrate & FRAME

Developed by the Technical Education team at the Web3 Foundation, this course provides a comprehensive overview of the Substrate blockchain framework and building blockchains.

Prerequisites

Before starting, it would be wise to either have experience with Rust, or have taken our Intro to Rust course. Substrate depends heavily on Rust, and it will be elaborated on extensively in this course.

This course will provide a solid foundation in navigating the creation and modification of custom blockchain modules, called pallets. This course will serve as an introduction into becoming a Substrate developer, the factors involved, as well as a guided, hands on portion to building a custom FRAME pallet.

In this course, you will:

  • Learn about FRAME, the primary mode of implementation for Substrate
  • Learn at high-level how storage, the runtime, and pallets work in FRAME
  • Learn how to build a basic pallet and run a custom blockchain via a template
  • Learn basic testing of a FRAME pallet
  • Learn best practices and important considerations when building a FRAME pallet
  • Build and deploy a basic, social-network like blockchain pallet called connect

Module 1 - Why Learn Substrate?

Module 2 - Substrate 101 - Overview of the Substrate Framework

Module 3 - Installing Dependencies & Node Template

Module 4 - Intro to Building a Custom FRAME Pallet

Module 5 - Building a Custom FRAME Pallet - Making our pallet actionable

Module 6 - Building a Custom FRAME Pallet - Deploying & Testing

Module 7 - Building a Custom FRAME Pallet - FRAME Best Practices

Module 8 - Substrate Deepdive

- + \ No newline at end of file diff --git a/docs/introweb3.html b/docs/introweb3.html index da6b2c2a0..de08ed2a3 100644 --- a/docs/introweb3.html +++ b/docs/introweb3.html @@ -5,13 +5,13 @@ Introduction to Web3 | Polkadot Education Initiative - +

Introduction to Web3

Developed by the Technical Education team at the Web3 Foundation, this course provides a beginner's introduction to Web3. Less trust, more truth.

Module 1 - What is Web3?

A brief history of Internet. Web2 vs Web3. Vision of Web3. Just a buzzword? How Blockchain technology backs Web3. Web3 Infrastructure today. Smart contracts. Just trust the code?

Module 2 - Cryptocurrencies and DeFi

What are Cryptocurrencies? Digital assets and Stable coins. Currency token vs Network utility token. Transactions and UTXO. Payments and Wallets. Anonymity. Exchanges. DEX and swapping. Collaterals. Liquidity providers. Hacks and scams.

Module 3 - NFTs and Metaverse

Fungibility vs Non-fungibility. Ownership. Creator economy.Property rights. Are NFTs just JPEGs? Community. Gaming.Composability. Digital Art. Social networks. Foundations of a multi(meta)verse?

Module 4 - DAOs and Governance

How decentralized and Autonomous can an organization be? Who governs a DAO? How hiring works? DAO treasury. A list of DAOs

Module 5 - Decentralized Computing

Smart contracts. Cloud storage. Cloud computing. Internet of Things. Networking. Limits.

Module 6 - Future of Web3

Opportunities and challenges. Culture. User experience. Middleware. Bridges. Unstoppable apps.

- + \ No newline at end of file diff --git a/docs/polkadotFAQ.html b/docs/polkadotFAQ.html index c63c07478..23a3ca5ce 100644 --- a/docs/polkadotFAQ.html +++ b/docs/polkadotFAQ.html @@ -5,7 +5,7 @@ Polkadot FAQ | Polkadot Education Initiative - + @@ -22,7 +22,7 @@ It also looks like somebody sent > 3500 DOT to the Treasury directly from an exchange wallet

How to find minimum active nomination over time?

Not sure if there is a site with that, but you can always look it up by querying chain state:

Go to https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.polkadot.io#/chainstate

Enter the block hash you want to check what it was

Query and see the result in Plancks

You could write a JavaScript script ( https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.polkadot.io#/js ) to get this over time.

I'd have to look up the details, but essentially, there was no limit at first. About a year ago, a minimum 40 DOT nomination was initiated, then 80, then 120, then 160 in February. With some major staking mechanism changes, the minimum to nominate over the last few months has been 10 DOT, albeit with a dynamic rewards minimum.

Nomination pools (now live on Kusama) should reduce this dramatically, down to 1 DOT or so.

How to see all the accounts on the network?

You could use Subsquid for this. Thanks to Lucas, our erstwhile Grants intern, for finding this: https://app.subsquid.io/aquarium/polkadot-balances

It's SQL - just change the "limit" variable to the number you want (note that it will take a while to run if you have a very large number here). The result will be in JSON, and amounts are shown in Plancks (like satoshis in Bitcoin, one DOT = 10_000_000_000 Plancks).

How to see the bags containing nominators?

Click on the "Bags" tab on that page, or go directly there with the URL - https://polkadot.js.org/apps/#/staking/bags

How to verify multisig?

if you ask them the addresses they used to make the account, you can make the multisig yourself and verify it's the same address it would be essentially impossible for them to generate a single-sig with the same address

Steps to do that:

  1. Add addresses to Address Book - https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama.api.onfinality.io%2Fpublic-ws#/addresses
  2. Go to Accounts and click "+ Multisig"
  3. Add the addresses and the threshold
  4. Give it a name
  5. Click "Create"

You will then have a new multisig in your Accounts tab (which you can't sign transactions with, but you can click the dots icon and get the address and verify it's what they say)

How is ideal staking rate calculated?

Depends on what you mean by a "good thing". The ideal rate was estimated as the ideal rate for the network to have locked in the staking mechanism for security. However, this also means less rewards for individual nominators, and the estimate is just that - an estimate. It could be that the network would operate optimally with less or more than the "ideal rate".

The ideal staking rate should vary based on the number of parachains (since some DOT/KSM will be locked for securing parachain slots), down to a minimum of 50%. Kusama reduces the ideal staking rate by 0.5 percentage points per parachain, and has many more parachains than Polkadot. This algorithm for modifying the ideal staking rate based on the number of parachains was not implemented on Polkadot; I'm not sure of the rationale behind that. Perhaps it ensures that there is much more locked to ensure a higher security level of Polkadot.

You can see more information here: https://wiki.polkadot.network/docs/learn-staking#inflation

Is Polkadot Decentralized?

How will the Polkadot eco-system and its para-chains be able to maintain its integrity as a decentralized and safe system that is effectively able to bypass and avoid over reaching regulatory scrutiny?

The Polkadot Network, and the DOT token which secures it, is fully decentralized. The Web3 Foundation took the hard road and worked with regulators throughout its launch to make sure that it was (and we believe still is) regulatory compliant in all jurisdictions.

The parachains themselves are responsible for their own regulatory compliance, including communications and disclosures, because they are not controlled by Polkadot, but the Web3 Foundation is always here to help on the educational front if have questions or if otherwise needed.

Is there any possibility that regulators and nation-states could force layer 0, layer 1 and layer 2 censorship within the Polkadot ecosystem and force (under threat of incarceration) developers to comply with their tyrannical oversight?

There's always a possibility of something like this, as we have already seen in various ways recently.

Can Polkadot and its para-chains truly become decoupled from the fiat ecosystem and be able to maintain the 5 pillars of crypto freedom (open, public, borderless, neutral, and censorship resistant)?

This is certainly our goal here at Web3 Foundation! We have a sign here at our headquarters that states our goal:

"We are building trustless, open-source infrastructure that empowers users to resist arbitrary authority and take back control of their sovereignty - a fully decentralized web."

That is certainly what I think, and hope, we and the rest of the ecosystem are building. =)

How to listen to account balance change?

https://polkadot.js.org/docs/api/examples/promise/listen-to-balance-change ?

I prefer using py-substrate so I can use Python instead of JavaScript, personally. There is an example of getting an account's information here: https://github.com/polkascan/py-substrate-interface#storage-queries

Why am I not receiving staking rewards?

If Polkadot staking were down, we'd be having real troubles, since we'd have no way of putting validators in the active set and thus unable to produce blocks.

I would check your accounts on Polkadot-JS App and if you are not getting rewards, see https://support.polkadot.network/support/solutions/articles/65000170805-why-am-i-not-getting-staking-rewards-

Why is Polkadot-JS UI not loading?

Perhaps try changing endpoints, browser, etc.

Or try using the IPFS version - https://cloudflare-ipfs.com/ipns/dotapps.io/#/staking

How to query block information without subscan?

You can query block information from a specific block using Network->Block Details (e.g. https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-rpc.polkadot.io#/explorer/query/14000000 ) and then look at the extrinsics for the block

Can the crowdloaned DOT be returned before the parachain slot lease expires?

Returning the DOT early is not possible going through the chain. Any DOT sent via crowdloan.contribute extrinsic will be locked up until the end of the lease (assuming they win a lease, of course).

DOT / KSM will definitely be returned.

To clarify, it's not just "customary", it's set by the rules of the chain. Anyone who is setting up a crowdloan can indicate the ending point, and after this ending point, anyone can refund the KSM to contributors to unsuccessful crowdloans (although this is usually done by the team that is running the crowdloan, technically anyone can do it).

Kusama auctions (mostly just for scheduling reasons, but also to give engineering teams time to see how the network is reacting before starting a new batch of auctions) have a bit of a gap between groups. Many crowdloans just set the ending point to be the end of the group, when really they should set it a bit earlier if they want to be perfectly optimized.

How can I see account balance over time?

Go to https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.polkadot.io#/js

Enter the blocks you want to check.

Enter the following JavaScript code and execute it. Note that the results may not be in chronological order, you can copy/paste and sort them in a text editor or the Unix sort command to do so.

blocks = [2105, 1205309, 1205349];

const user = '1WG3jyNqniQMRZGQUc7QD2kVLT8hkRPGMSqAb5XYQM1UDxN';

blocks.forEach(calc);

async function calc(blockNum){
const blockHash = await api.rpc.chain.getBlockHash(blockNum);
const { data: { free: prev } } = await api.query.system.account.at(blockHash, user);
console.log('Block', blockNum, ': user had a balance of', prev.toHuman());
}

How to see the extrinsics supported by Ledger?

If you want to use Ledger, you will need the XL version of the app, as the light version does not support the necessary extrinsics. See https://github.com/zondax/ledger-polkadot#voterlist

Staking rewards collector gets API rate limit exceeded error?

Every time I try to run the staking-rewards-collector, I get an error from the Subscan API that the API rate limit is exceeded. Anyone know how I can slow down the query speed or something to get around this?

Solution: enter your own Subscan API key as a new JSON file element "subscan_apikey" in the config file.

Transferring Polkadot-JS Apps Accounts/Addresses From One Computer to Another

Note: This will overwrite any existing accounts with the same pubkey on your new computer. This generally should not make a difference (since it can still access the same account), but might if you have e.g. an account which was stored externally in the extension on the old computer but was created directly in browser on the new one.

This has been tested on Brave and Chrome, but not other browsers.

  1. Go to Polkadot-JS Apps
  2. Go to JavaScript console (on Brave, NOT on Polkadot-JS)
  3. Type in the command JSON.stringify(localStorage)
  4. Copy and paste the returned string to a text editor.
  5. Check that the string you pasted both begins and ends with a tick mark ('). If not, add one to the beginning and end.
  6. Save and send that JSON file to the new computer.
  7. On new computer, go to Polkadot-JS Apps
  8. Open the Javascript console
  9. Set a variable raw equal to the JSON you created =
raw = ... copy-pasted json from original computer ...
  1. Run the following code:
accounts = JSON.parse(raw);
for (var key in accounts) {
if (accounts.hasOwnProperty(key)) {
val = JSON.stringify(accounts[key]).replace(/\\/g,'').slice(1,-1);
console.log(key + " -> " + val);
localStorage.setItem(key, val);
}
}
  1. Refresh Polkadot-JS App browser and check Accounts and Addresses pages. All of your accounts and addresses should now be available.

Does everything need to be on the blockchain to be decentralized?

IPFS is decentralized, even though it's not using blockchain ( https://ipfs.tech/ ) - it's not at all the same as putting something on a Google Cloud server. Same with Crust.

Conversely, on-chain does not necessarily mean decentralized ( see this article from a former W3F Tech Ed team member - https://bitfalls.com/hr/2020/05/25/opinion-on-private-blockchains/ ).

It's important to realize that technically, a blockchain is just a linked list with hash pointers. There's nothing about the blockchain data structure itself that promotes decentralization, just immutability. You can use this data structure with other technology to make something decentralized and there are a lot of benefits to doing so.

Plenty of things can happen "off-chain" while still being decentralized. The chain can even prove that computations are happening correctly or are not being manipulated (see zero-knowledge proofs, various L2s, Substrate's off-chain workers, etc.)

Regarding your question, I disagree completely. Yes, there are projects which are nothing more than money grabs, and there are chains which pay only lip service to decentralization. But personally, I see massive potential in having code and data which is provably immutable and transparent. I wouldn't be in this space otherwise.

If you are interested in digging into all of this further, my lectures on blockchain fundamentals are (in my biased opinion, of course!) a good place to start - https://mooc.web3.foundation/

Not to mention, you CAN use remarks to store data entirely on-chain if you want ( just like you could e.g. use OP_RETURN on Bitcoin). I'm just saying that if this causes scalability issues ( a common problem due to the blockchain model itself ), then there are other ways to do it and still remain decentralized.

What is the difference between XCM and XCMP?

Yes. XCM is the cross-consensus message format; XCMP is a cross-chain messaging protocol. Think of sending a tax form to your government via the postal system - the form you send is the FORMAT of the message you are sending them, a very specific format; the mail system is the PROTOCOL of how it gets there.

Why am I stuck with the same validator?

It's sometimes difficult to understand why Phragmen does what it does. It's a complicated algorithm - https://wiki.polkadot.network/docs/learn-phragmen. I've definitely had times where I was stuck with the same validator for long periods of time (in one case, much to my detriment, as the validator had changed their commission rate to 100%... but I digress...)

Generally, however, the reason this happens is because your amount of DOT/KSM fits very well to help reduce the variance (which is one of the big goals of Phragmen). So for example, if the mean amount of DOT on a validator is 1_000_000, and you are nominating that validator with 10_000 DOT and another account is nominating it with 990_000, you both will often find yourself nominating that validator since they "fit" so well.

That said, nominating is essentially an "approval" process - you are saying that you would be OK with nominating any of the validators you select for nomination. If you're not OK with actively nominating that validator, on a regular basis or not, you shouldn't be nominating it.

Why can't I see certain pages on Polkadot-JS, such as Extrinsics or Sign and Verify?

These pages require at least one account on Polkadot-JS. Go the Accounts page and add an account, and you should see these in the header bar.

Note that these pages may load if you navigate directly to them (e.g. https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-rpc.polkadot.io#/extrinsics), but you won't be able to do anything there.

Why did my account make a duplicate transaction?

Two submissions of the same extrinsic were made to the blockchain, about six hours apart. I'm not sure of why this occurred (and I'm not familiar with Safepal), but I can explain what happened.

First, note that unlike most blockchains, a transaction hash is not a globally unique identifier in Polkadot (see the wiki: https://wiki.polkadot.network/docs/build-protocol-info#unique-identifiers-for-extrinsics ) The extrinsic ID is (once a block is finalized, of course). So the fact that the tx hash is the same means that the exact same transaction was submitted multiple times. Looking at the history of that account, it looks to be about six hours apart.

Also note that this transaction was submitted as immortal ( https://polkadot.subscan.io/extrinsic/0xccbbea4214ec68462b0581ca2c106118b2c2e6043f4cb3c75ea7e77bb15d810a ) - something that is specifically warned against, as it can allow replay attacks ( https://wiki.polkadot.network/docs/build-protocol-info#transaction-mortality ) . This is generally something in the control of the wallet, not the user.

During the first tx, the account was reaped (i.e. all DOT was sent out from it) . You can see the event here: https://polkadot.subscan.io/extrinsic/0xccbbea4214ec68462b0581ca2c106118b2c2e6043f4cb3c75ea7e77bb15d810a?event=10906629-18

Then a day later, the account was resurrected by sending in a little under 25 DOT - https://polkadot.subscan.io/extrinsic/0xfaed7db64a649d7979bf2d59d785495b6a95f6c31afae797ca4136b43b6248f0

For some reason, the original immortal transaction was then re-submitted. Now the account existed again, and this was a valid immortal transaction, so it got executed again.

Why do I get call filtered error?

system.ExtrinsicFailed system.CallFiltered

usually system.CallFiltered is because your origin can't do that or just all calls to that module are blocked out. I'd talk to the specific team that implemented it.

- + \ No newline at end of file diff --git a/index.html b/index.html index e09c7cff3..396e2956c 100644 --- a/index.html +++ b/index.html @@ -5,13 +5,13 @@ Polkadot Education Initiative | Polkadot Education Initiative - +

Polkadot Education Initiative

Powered by the community

Overview

Blockchain and Web3 Courses

Blockchain and Web3 Courses

The Polkadot Wiki

The Polkadot Wiki

Our Tutorials

Our Tutorials

- + \ No newline at end of file