diff --git a/404.html b/404.html index ab8acc97e..6f061e977 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/57376d8e.47990fc1.js b/assets/js/57376d8e.47990fc1.js deleted file mode 100644 index 3f7f389e2..000000000 --- a/assets/js/57376d8e.47990fc1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[5864],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var r=n(7294);function a(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 r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),p=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=p(e.components);return r.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,h=u["".concat(s,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(h,l(l({ref:t},c),{},{components:n})):r.createElement(h,l({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,l=new Array(i);l[0]=m;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[u]="string"==typeof e?e:a,l[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>d,frontMatter:()=>i,metadata:()=>o,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const i={id:"explore-pallet-template",title:"Exploring the pallet template",sidebar_label:"Exploring the pallet template",description:"Explore the pallet template, and get a sneak peek into what is to come next!"},l=void 0,o={unversionedId:"Substrate/section3/explore-pallet-template",id:"Substrate/section3/explore-pallet-template",title:"Exploring the pallet template",description:"Explore the pallet template, and get a sneak peek into what is to come next!",source:"@site/docs/Substrate/section3/explore-pallet-template.md",sourceDirName:"Substrate/section3",slug:"/Substrate/section3/explore-pallet-template",permalink:"/docs/Substrate/section3/explore-pallet-template",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section3/explore-pallet-template.md",tags:[],version:"current",lastUpdatedBy:"bader y",lastUpdatedAt:1688478970,formattedLastUpdatedAt:"Jul 4, 2023",frontMatter:{id:"explore-pallet-template",title:"Exploring the pallet template",sidebar_label:"Exploring the pallet template",description:"Explore the pallet template, and get a sneak peek into what is to come next!"},sidebar:"substrate",previous:{title:"Node Template Tour & Overview",permalink:"/docs/Substrate/section3/node-template-tour"},next:{title:"Intro to Building a Custom FRAME Pallet",permalink:"/docs/Substrate/section4/"}},s={},p=[{value:"lib.rs",id:"librs",level:2},{value:"mock.rs and tests.rs",id:"mockrs-and-testsrs",level:2},{value:"benchmarking.rs and weights.rs",id:"benchmarkingrs-and-weightsrs",level:2}],c={toc:p},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"We will spend most of our time in the ",(0,a.kt)("inlineCode",{parentName:"p"},"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."),(0,a.kt)("p",null,"Our pallet, called ",(0,a.kt)("inlineCode",{parentName:"p"},"pallet-connect"),", defines our custom logic that is then enabled by our runtime within the node template."),(0,a.kt)("h2",{id:"librs"},"lib.rs"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"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:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[pallet::pallet]\npub struct Pallet(_);\n")),(0,a.kt)("p",null,"We will go into more detail on the structure of ",(0,a.kt)("inlineCode",{parentName:"p"},"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 ",(0,a.kt)("inlineCode",{parentName:"p"},"construct_runtime!")," macro is responsible for defining and constructing a runtime based on the collection of pallets and their respective configurations."),(0,a.kt)("h2",{id:"mockrs-and-testsrs"},"mock.rs and tests.rs"),(0,a.kt)("p",null,"In most pallets, including our template, you will also see the following files: ",(0,a.kt)("inlineCode",{parentName:"p"},"mock.rs")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"tests.rs"),". As one may assume, they are used for two purposes:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"mock.rs")," is used for configuring a test environment, i.e., a test runtime configured for unit testing."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"tests.rs")," is where unit tests reside and act on the values and configuration defined within ",(0,a.kt)("inlineCode",{parentName:"li"},"mock.rs"))),(0,a.kt)("h2",{id:"benchmarkingrs-and-weightsrs"},"benchmarking.rs and weights.rs"),(0,a.kt)("p",null,"Benchmarking and weights measure an extrinsic's performance, or ",(0,a.kt)("strong",{parentName:"p"},"weight"),". Substrate is built around the concept of ",(0,a.kt)("strong",{parentName:"p"},"weight"),", which measures how much computation is required to execute it on-chain. Using benchmarking, weight can be assigned to each extrinsic, and the ",(0,a.kt)("inlineCode",{parentName:"p"},"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."),(0,a.kt)("p",null,"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."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/57376d8e.96be6b6f.js b/assets/js/57376d8e.96be6b6f.js new file mode 100644 index 000000000..ef25397da --- /dev/null +++ b/assets/js/57376d8e.96be6b6f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[5864],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var r=n(7294);function a(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 r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),p=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=p(e.components);return r.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),u=p(n),m=a,h=u["".concat(s,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(h,l(l({ref:t},c),{},{components:n})):r.createElement(h,l({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,l=new Array(i);l[0]=m;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[u]="string"==typeof e?e:a,l[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>d,frontMatter:()=>i,metadata:()=>o,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const i={id:"explore-pallet-template",title:"Exploring the pallet template",sidebar_label:"Exploring the pallet template",description:"Explore the pallet template, and get a sneak peek into what is to come next!"},l=void 0,o={unversionedId:"Substrate/section3/explore-pallet-template",id:"Substrate/section3/explore-pallet-template",title:"Exploring the pallet template",description:"Explore the pallet template, and get a sneak peek into what is to come next!",source:"@site/docs/Substrate/section3/explore-pallet-template.md",sourceDirName:"Substrate/section3",slug:"/Substrate/section3/explore-pallet-template",permalink:"/docs/Substrate/section3/explore-pallet-template",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section3/explore-pallet-template.md",tags:[],version:"current",lastUpdatedBy:"bader y",lastUpdatedAt:1688478970,formattedLastUpdatedAt:"Jul 4, 2023",frontMatter:{id:"explore-pallet-template",title:"Exploring the pallet template",sidebar_label:"Exploring the pallet template",description:"Explore the pallet template, and get a sneak peek into what is to come next!"},sidebar:"substrate",previous:{title:"Node Template Tour & Overview",permalink:"/docs/Substrate/section3/node-template-tour"},next:{title:"Custom FRAME Pallet",permalink:"/docs/Substrate/section4/"}},s={},p=[{value:"lib.rs",id:"librs",level:2},{value:"mock.rs and tests.rs",id:"mockrs-and-testsrs",level:2},{value:"benchmarking.rs and weights.rs",id:"benchmarkingrs-and-weightsrs",level:2}],c={toc:p},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"We will spend most of our time in the ",(0,a.kt)("inlineCode",{parentName:"p"},"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."),(0,a.kt)("p",null,"Our pallet, called ",(0,a.kt)("inlineCode",{parentName:"p"},"pallet-connect"),", defines our custom logic that is then enabled by our runtime within the node template."),(0,a.kt)("h2",{id:"librs"},"lib.rs"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"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:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[pallet::pallet]\npub struct Pallet(_);\n")),(0,a.kt)("p",null,"We will go into more detail on the structure of ",(0,a.kt)("inlineCode",{parentName:"p"},"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 ",(0,a.kt)("inlineCode",{parentName:"p"},"construct_runtime!")," macro is responsible for defining and constructing a runtime based on the collection of pallets and their respective configurations."),(0,a.kt)("h2",{id:"mockrs-and-testsrs"},"mock.rs and tests.rs"),(0,a.kt)("p",null,"In most pallets, including our template, you will also see the following files: ",(0,a.kt)("inlineCode",{parentName:"p"},"mock.rs")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"tests.rs"),". As one may assume, they are used for two purposes:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"mock.rs")," is used for configuring a test environment, i.e., a test runtime configured for unit testing."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"tests.rs")," is where unit tests reside and act on the values and configuration defined within ",(0,a.kt)("inlineCode",{parentName:"li"},"mock.rs"))),(0,a.kt)("h2",{id:"benchmarkingrs-and-weightsrs"},"benchmarking.rs and weights.rs"),(0,a.kt)("p",null,"Benchmarking and weights measure an extrinsic's performance, or ",(0,a.kt)("strong",{parentName:"p"},"weight"),". Substrate is built around the concept of ",(0,a.kt)("strong",{parentName:"p"},"weight"),", which measures how much computation is required to execute it on-chain. Using benchmarking, weight can be assigned to each extrinsic, and the ",(0,a.kt)("inlineCode",{parentName:"p"},"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."),(0,a.kt)("p",null,"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."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/58d818ae.179b6e00.js b/assets/js/58d818ae.179b6e00.js new file mode 100644 index 000000000..ed5a62caa --- /dev/null +++ b/assets/js/58d818ae.179b6e00.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[6845],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function c(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),s=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(l.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,c=e.originalType,l=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),d=s(r),m=a,f=d["".concat(l,".").concat(m)]||d[m]||p[m]||c;return r?n.createElement(f,i(i({ref:t},u),{},{components:r})):n.createElement(f,i({ref:t},u))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var c=r.length,i=new Array(c);i[0]=m;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o[d]="string"==typeof e?e:a,i[1]=o;for(var s=2;s{r.d(t,{Z:()=>b});var n=r(7294),a=r(6010),c=r(2802),i=r(9960),o=r(3919),l=r(5999);const s={cardContainer:"cardContainer_fWXF",cardTitle:"cardTitle_rnsV",cardDescription:"cardDescription_PWke"};function u(e){let{href:t,children:r}=e;return n.createElement(i.Z,{href:t,className:(0,a.Z)("card padding--lg",s.cardContainer)},r)}function d(e){let{href:t,icon:r,title:c,description:i}=e;return n.createElement(u,{href:t},n.createElement("h2",{className:(0,a.Z)("text--truncate",s.cardTitle),title:c},r," ",c),i&&n.createElement("p",{className:(0,a.Z)("text--truncate",s.cardDescription),title:i},i))}function p(e){let{item:t}=e;const r=(0,c.Wl)(t);return r?n.createElement(d,{href:r,icon:"\ud83d\uddc3\ufe0f",title:t.label,description:t.description??(0,l.I)({message:"{count} items",id:"theme.docs.DocCard.categoryDescription",description:"The default description for a category card in the generated index about how many items this category includes"},{count:t.items.length})}):null}function m(e){let{item:t}=e;const r=(0,o.Z)(t.href)?"\ud83d\udcc4\ufe0f":"\ud83d\udd17",a=(0,c.xz)(t.docId??void 0);return n.createElement(d,{href:t.href,icon:r,title:t.label,description:t.description??a?.description})}function f(e){let{item:t}=e;switch(t.type){case"link":return n.createElement(m,{item:t});case"category":return n.createElement(p,{item:t});default:throw new Error(`unknown item type ${JSON.stringify(t)}`)}}function y(e){let{className:t}=e;const r=(0,c.jA)();return n.createElement(b,{items:r.items,className:t})}function b(e){const{items:t,className:r}=e;if(!t)return n.createElement(y,e);const i=(0,c.MN)(t);return n.createElement("section",{className:(0,a.Z)("row",r)},i.map(((e,t)=>n.createElement("article",{key:t,className:"col col--6 margin-bottom--lg"},n.createElement(f,{item:e})))))}},5406:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var n=r(7462),a=(r(7294),r(3905)),c=r(2991);const i={title:"Custom FRAME Pallet"},o=void 0,l={unversionedId:"Substrate/section4/index",id:"Substrate/section4/index",title:"Custom FRAME Pallet",description:"",source:"@site/docs/Substrate/section4/index.md",sourceDirName:"Substrate/section4",slug:"/Substrate/section4/",permalink:"/docs/Substrate/section4/",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section4/index.md",tags:[],version:"current",lastUpdatedBy:"Radha",lastUpdatedAt:1691089637,formattedLastUpdatedAt:"Aug 3, 2023",frontMatter:{title:"Custom FRAME Pallet"},sidebar:"substrate",previous:{title:"Exploring the pallet template",permalink:"/docs/Substrate/section3/explore-pallet-template"},next:{title:"Adjusting Pallet Config & Runtime Overview",permalink:"/docs/Substrate/section4/pallet-config"}},s={},u=[],d={toc:u},p="wrapper";function m(e){let{components:t,...r}=e;return(0,a.kt)(p,(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)(c.Z,{mdxType:"DocCardList"}))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/58d818ae.758ee7dd.js b/assets/js/58d818ae.758ee7dd.js deleted file mode 100644 index 2a4164d95..000000000 --- a/assets/js/58d818ae.758ee7dd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[6845],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(7294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var l=n.createContext({}),s=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(l.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),d=s(r),m=i,f=d["".concat(l,".").concat(m)]||d[m]||p[m]||a;return r?n.createElement(f,o(o({ref:t},u),{},{components:r})):n.createElement(f,o({ref:t},u))}));function f(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[d]="string"==typeof e?e:i,o[1]=c;for(var s=2;s{r.d(t,{Z:()=>b});var n=r(7294),i=r(6010),a=r(2802),o=r(9960),c=r(3919),l=r(5999);const s={cardContainer:"cardContainer_fWXF",cardTitle:"cardTitle_rnsV",cardDescription:"cardDescription_PWke"};function u(e){let{href:t,children:r}=e;return n.createElement(o.Z,{href:t,className:(0,i.Z)("card padding--lg",s.cardContainer)},r)}function d(e){let{href:t,icon:r,title:a,description:o}=e;return n.createElement(u,{href:t},n.createElement("h2",{className:(0,i.Z)("text--truncate",s.cardTitle),title:a},r," ",a),o&&n.createElement("p",{className:(0,i.Z)("text--truncate",s.cardDescription),title:o},o))}function p(e){let{item:t}=e;const r=(0,a.Wl)(t);return r?n.createElement(d,{href:r,icon:"\ud83d\uddc3\ufe0f",title:t.label,description:t.description??(0,l.I)({message:"{count} items",id:"theme.docs.DocCard.categoryDescription",description:"The default description for a category card in the generated index about how many items this category includes"},{count:t.items.length})}):null}function m(e){let{item:t}=e;const r=(0,c.Z)(t.href)?"\ud83d\udcc4\ufe0f":"\ud83d\udd17",i=(0,a.xz)(t.docId??void 0);return n.createElement(d,{href:t.href,icon:r,title:t.label,description:t.description??i?.description})}function f(e){let{item:t}=e;switch(t.type){case"link":return n.createElement(m,{item:t});case"category":return n.createElement(p,{item:t});default:throw new Error(`unknown item type ${JSON.stringify(t)}`)}}function y(e){let{className:t}=e;const r=(0,a.jA)();return n.createElement(b,{items:r.items,className:t})}function b(e){const{items:t,className:r}=e;if(!t)return n.createElement(y,e);const o=(0,a.MN)(t);return n.createElement("section",{className:(0,i.Z)("row",r)},o.map(((e,t)=>n.createElement("article",{key:t,className:"col col--6 margin-bottom--lg"},n.createElement(f,{item:e})))))}},5406:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>c,default:()=>m,frontMatter:()=>o,metadata:()=>l,toc:()=>u});var n=r(7462),i=(r(7294),r(3905)),a=r(2991);const o={title:"Intro to Building a Custom FRAME Pallet"},c=void 0,l={unversionedId:"Substrate/section4/index",id:"Substrate/section4/index",title:"Intro to Building a Custom FRAME Pallet",description:"",source:"@site/docs/Substrate/section4/index.md",sourceDirName:"Substrate/section4",slug:"/Substrate/section4/",permalink:"/docs/Substrate/section4/",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section4/index.md",tags:[],version:"current",lastUpdatedBy:"Bader Youssef",lastUpdatedAt:1687289849,formattedLastUpdatedAt:"Jun 20, 2023",frontMatter:{title:"Intro to Building a Custom FRAME Pallet"},sidebar:"substrate",previous:{title:"Exploring the pallet template",permalink:"/docs/Substrate/section3/explore-pallet-template"},next:{title:"Adjusting Pallet Config & Runtime Overview",permalink:"/docs/Substrate/section4/pallet-config"}},s={},u=[],d={toc:u},p="wrapper";function m(e){let{components:t,...r}=e;return(0,i.kt)(p,(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)(a.Z,{mdxType:"DocCardList"}))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.f0309436.js b/assets/js/935f2afb.7a1dcd1f.js similarity index 92% rename from assets/js/935f2afb.f0309436.js rename to assets/js/935f2afb.7a1dcd1f.js index f732b0fd3..01009af72 100644 --- a/assets/js/935f2afb.f0309436.js +++ b/assets/js/935f2afb.7a1dcd1f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"blockchain":[{"type":"link","label":"Blockchain Fundamentals","href":"/docs/introblock","docId":"introblock"},{"type":"category","label":"Introduction to Blockchain","collapsed":false,"items":[{"type":"link","label":"What is a Blockchain?","href":"/docs/Blockchain/Module1/blockchain","docId":"Blockchain/Module1/blockchain"},{"type":"link","label":"History of Money and Trust","href":"/docs/Blockchain/Module1/money-trust","docId":"Blockchain/Module1/money-trust"},{"type":"link","label":"Properties of Money","href":"/docs/Blockchain/Module1/money-properties","docId":"Blockchain/Module1/money-properties"},{"type":"link","label":"Dawn of Digital Money","href":"/docs/Blockchain/Module1/digital-money","docId":"Blockchain/Module1/digital-money"},{"type":"link","label":"Introduction to Bitcoin","href":"/docs/Blockchain/Module1/bitcoin","docId":"Blockchain/Module1/bitcoin"},{"type":"link","label":"Introduction to Ethereum","href":"/docs/Blockchain/Module1/ethereum","docId":"Blockchain/Module1/ethereum"},{"type":"link","label":"Introduction to Web3","href":"/docs/Blockchain/Module1/web3","docId":"Blockchain/Module1/web3"}],"collapsible":true},{"type":"category","label":"Blockchain and Cryptography","items":[{"type":"link","label":"Cryptography and Blockchain","href":"/docs/Blockchain/Module2/cryptography","docId":"Blockchain/Module2/cryptography"},{"type":"link","label":"Digital Signatures and Crypto-wallets","href":"/docs/Blockchain/Module2/keypair","docId":"Blockchain/Module2/keypair"},{"type":"link","label":"Cryptographic Hashing","href":"/docs/Blockchain/Module2/hash","docId":"Blockchain/Module2/hash"},{"type":"link","label":"Blockchain Datastructures","href":"/docs/Blockchain/Module2/datastructures","docId":"Blockchain/Module2/datastructures"},{"type":"link","label":"Trust issues","href":"/docs/Blockchain/Module2/trust","docId":"Blockchain/Module2/trust"},{"type":"link","label":"How Invincible is Bitcoin Network?","href":"/docs/Blockchain/Module2/invincible","docId":"Blockchain/Module2/invincible"},{"type":"link","label":"Decentralization Trilemma","href":"/docs/Blockchain/Module2/decentralization","docId":"Blockchain/Module2/decentralization"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Block Mining and Finality","items":[{"type":"link","label":"How Bitcoin blocks are mined?","href":"/docs/Blockchain/Module3/mining","docId":"Blockchain/Module3/mining"},{"type":"link","label":"Bitcoin mining hardware","href":"/docs/Blockchain/Module3/mining-hardware","docId":"Blockchain/Module3/mining-hardware"},{"type":"link","label":"Bitcoin Hashpower","href":"/docs/Blockchain/Module3/hashpower","docId":"Blockchain/Module3/hashpower"},{"type":"link","label":"Proof of Work","href":"/docs/Blockchain/Module3/PoW","docId":"Blockchain/Module3/PoW"},{"type":"link","label":"Hardforks of Blockchain","href":"/docs/Blockchain/Module3/forking","docId":"Blockchain/Module3/forking"},{"type":"link","label":"Block finality","href":"/docs/Blockchain/Module3/finality","docId":"Blockchain/Module3/finality"},{"type":"link","label":"Proof of Staking","href":"/docs/Blockchain/Module3/PoS","docId":"Blockchain/Module3/PoS"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Nodes and Networking","items":[{"type":"link","label":"Introduction to Distributed Systems","href":"/docs/Blockchain/Module4/distributed","docId":"Blockchain/Module4/distributed"},{"type":"link","label":"Network Consensus","href":"/docs/Blockchain/Module4/consensus","docId":"Blockchain/Module4/consensus"},{"type":"link","label":"Network Stack","href":"/docs/Blockchain/Module4/networkstack","docId":"Blockchain/Module4/networkstack"},{"type":"link","label":"How do Blockchain nodes communicate?","href":"/docs/Blockchain/Module4/gossip","docId":"Blockchain/Module4/gossip"},{"type":"link","label":"Types of Network Nodes","href":"/docs/Blockchain/Module4/nodes","docId":"Blockchain/Module4/nodes"},{"type":"link","label":"Networking Challenges and Opportunities","href":"/docs/Blockchain/Module4/network-challenges","docId":"Blockchain/Module4/network-challenges"},{"type":"link","label":"Light clients and Trustless Networks","href":"/docs/Blockchain/Module4/lightclients","docId":"Blockchain/Module4/lightclients"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Layers and Applications","items":[{"type":"link","label":"What are Blockchain Layers?","href":"/docs/Blockchain/Module5/layers","docId":"Blockchain/Module5/layers"},{"type":"link","label":"Layer 0 Blockchains","href":"/docs/Blockchain/Module5/layer0","docId":"Blockchain/Module5/layer0"},{"type":"link","label":"Layer 1 Blockchains","href":"/docs/Blockchain/Module5/layer1","docId":"Blockchain/Module5/layer1"},{"type":"link","label":"Layer 2 Blockchains","href":"/docs/Blockchain/Module5/layer2","docId":"Blockchain/Module5/layer2"},{"type":"link","label":"Enterprise Blockchains","href":"/docs/Blockchain/Module5/enterprise","docId":"Blockchain/Module5/enterprise"},{"type":"link","label":"ZK Proofs and Applications","href":"/docs/Blockchain/Module5/zk-proofs","docId":"Blockchain/Module5/zk-proofs"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Web3 and the future of Blockchain","items":[{"type":"link","label":"Introduction to Web3?","href":"/docs/Blockchain/Module6/web3","docId":"Blockchain/Module6/web3"},{"type":"link","label":"Cryptocurrencies and DeFi","href":"/docs/Blockchain/Module6/crypto-defi","docId":"Blockchain/Module6/crypto-defi"},{"type":"link","label":"NFTs and Metaverse","href":"/docs/Blockchain/Module6/nft-meta","docId":"Blockchain/Module6/nft-meta"},{"type":"link","label":"DAOs and Governance","href":"/docs/Blockchain/Module6/dao","docId":"Blockchain/Module6/dao"},{"type":"link","label":"Decentralized Computing","href":"/docs/Blockchain/Module6/computing","docId":"Blockchain/Module6/computing"},{"type":"link","label":"Future of Web3","href":"/docs/Blockchain/Module6/future-web3","docId":"Blockchain/Module6/future-web3"}],"collapsed":true,"collapsible":true},{"type":"link","label":"Learn more","href":"https://mooc.web3.foundation/"}],"polkadot":[{"type":"link","label":"Introduction to Polkadot","href":"/docs/introdot","docId":"introdot"},{"type":"category","label":"What is Polkadot?","collapsed":false,"items":[{"type":"link","label":"What is Polkadot?","href":"/docs/Polkadot/Module1/polkadot","docId":"Polkadot/Module1/polkadot"},{"type":"link","label":"Architecture of Polkadot","href":"/docs/Polkadot/Module1/architecture","docId":"Polkadot/Module1/architecture"},{"type":"link","label":"Vision of Polkadot","href":"/docs/Polkadot/Module1/polkadotvision","docId":"Polkadot/Module1/polkadotvision"},{"type":"link","label":"Features of Polkadot","href":"/docs/Polkadot/Module1/features","docId":"Polkadot/Module1/features"},{"type":"link","label":"Becoming a Parachain on Polkadot","href":"/docs/Polkadot/Module1/becomeparachain","docId":"Polkadot/Module1/becomeparachain"}],"collapsible":true},{"type":"category","label":"Interact with Polkadot","collapsed":false,"items":[{"type":"link","label":"Create an Account","href":"/docs/Polkadot/Module2/account","docId":"Polkadot/Module2/account"},{"type":"link","label":"DOT Token Utility","href":"/docs/Polkadot/Module2/dotutility","docId":"Polkadot/Module2/dotutility"},{"type":"link","label":"Exploring Polkadot Network","href":"/docs/Polkadot/Module2/explorenetwork","docId":"Polkadot/Module2/explorenetwork"},{"type":"link","label":"On-Chain Governance","href":"/docs/Polkadot/Module2/governance","docId":"Polkadot/Module2/governance"},{"type":"link","label":"Treasury","href":"/docs/Polkadot/Module2/treasury","docId":"Polkadot/Module2/treasury"}],"collapsible":true},{"type":"category","label":"Consensus and Security","items":[{"type":"link","label":"Polkadot Network Consensus","href":"/docs/Polkadot/Module3/consensus","docId":"Polkadot/Module3/consensus"},{"type":"link","label":"Nominated Proof of Staking","href":"/docs/Polkadot/Module3/npos","docId":"Polkadot/Module3/npos"},{"type":"link","label":"Validators and Nominators","href":"/docs/Polkadot/Module3/maintainers","docId":"Polkadot/Module3/maintainers"},{"type":"link","label":"Securing the Network","href":"/docs/Polkadot/Module3/sharedsecurity","docId":"Polkadot/Module3/sharedsecurity"},{"type":"link","label":"Security and Consensus Improvements","href":"/docs/Polkadot/Module3/securityimprovements","docId":"Polkadot/Module3/securityimprovements"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Cryptography and Networking","items":[{"type":"link","label":"Cryptography","href":"/docs/Polkadot/Module4/cryptography","docId":"Polkadot/Module4/cryptography"},{"type":"link","label":"Networking","href":"/docs/Polkadot/Module4/networking","docId":"Polkadot/Module4/networking"},{"type":"link","label":"Nodes on Polkadot Network","href":"/docs/Polkadot/Module4/nodes","docId":"Polkadot/Module4/nodes"},{"type":"link","label":"Path of a Parachain Block","href":"/docs/Polkadot/Module4/parachainblock","docId":"Polkadot/Module4/parachainblock"},{"type":"link","label":"Decentralization of Network","href":"/docs/Polkadot/Module4/decentralization","docId":"Polkadot/Module4/decentralization"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Interoperability and Scalability","items":[{"type":"link","label":"Interoperability","href":"/docs/Polkadot/Module5/interoperability","docId":"Polkadot/Module5/interoperability"},{"type":"link","label":"Polkadot Bridges","href":"/docs/Polkadot/Module5/bridges","docId":"Polkadot/Module5/bridges"},{"type":"link","label":"Parachains","href":"/docs/Polkadot/Module5/parachains","docId":"Polkadot/Module5/parachains"},{"type":"link","label":"Scalability","href":"/docs/Polkadot/Module5/scalability","docId":"Polkadot/Module5/scalability"},{"type":"link","label":"Polkadot Architecture Improvements","href":"/docs/Polkadot/Module5/architectureimprovements","docId":"Polkadot/Module5/architectureimprovements"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Polkadot for Developers","items":[{"type":"link","label":"Rust for Blockchain Development","href":"/docs/Polkadot/Module6/rust","docId":"Polkadot/Module6/rust"},{"type":"link","label":"Introduction to Substrate","href":"/docs/Polkadot/Module6/substrate","docId":"Polkadot/Module6/substrate"},{"type":"link","label":"Polkadot Test Networks","href":"/docs/Polkadot/Module6/testnets","docId":"Polkadot/Module6/testnets"},{"type":"link","label":"Polkadot JS","href":"/docs/Polkadot/Module6/polkadotjs","docId":"Polkadot/Module6/polkadotjs"},{"type":"link","label":"Developer Community","href":"/docs/Polkadot/Module6/developers","docId":"Polkadot/Module6/developers"}],"collapsed":true,"collapsible":true},{"type":"link","label":"Learn more","href":"https://wiki.polkadot.network/"}],"rust":[{"type":"link","label":"Course Appendix","href":"/docs/Rust/rust-appendix","docId":"Rust/rust-appendix"},{"type":"link","label":"Course Syllabus","href":"/docs/introrust","docId":"introrust"},{"type":"category","label":"Why Rust?","items":[{"type":"link","label":"What is Rust?","href":"/docs/Rust/section1/what-is-rust","docId":"Rust/section1/what-is-rust"},{"type":"link","label":"Why Learn Rust?","href":"/docs/Rust/section1/why-rust","docId":"Rust/section1/why-rust"},{"type":"link","label":"WASM TLDR","href":"/docs/Rust/section1/wasm-tldr","docId":"Rust/section1/wasm-tldr"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section1/"},{"type":"link","label":"Installing & Setting up a Rust Developer Environment","href":"/docs/Rust/setup/installation","docId":"Rust/setup/installation"},{"type":"category","label":"Rust 101 - Intro to Basic Rust","items":[{"type":"link","label":"Variables & Mutability","href":"/docs/Rust/section2/variables-mutability","docId":"Rust/section2/variables-mutability"},{"type":"link","label":"The Heap vs. The Stack","href":"/docs/Rust/section2/heap-vs-stack","docId":"Rust/section2/heap-vs-stack"},{"type":"link","label":"Data Types","href":"/docs/Rust/section2/data-types","docId":"Rust/section2/data-types"},{"type":"link","label":"Functions & Comments","href":"/docs/Rust/section2/functions-comments","docId":"Rust/section2/functions-comments"},{"type":"link","label":"Loops & Basic Logic Flows","href":"/docs/Rust/section2/loops","docId":"Rust/section2/loops"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section2/"},{"type":"category","label":"Intro to Intermediate Rust: Ownership, Borrowing, & Slices","items":[{"type":"link","label":"The Ownership Model","href":"/docs/Rust/section3/ownership","docId":"Rust/section3/ownership"},{"type":"link","label":"Borrowing & References","href":"/docs/Rust/section3/borrowing","docId":"Rust/section3/borrowing"},{"type":"link","label":"Slices","href":"/docs/Rust/section3/slices","docId":"Rust/section3/slices"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section3/"},{"type":"category","label":"Intro to Intermediate Rust: Enums & Matching Patterns","items":[{"type":"link","label":"Enums","href":"/docs/Rust/section4/enums","docId":"Rust/section4/enums"},{"type":"link","label":"Panic! in Rust","href":"/docs/Rust/section4/panic","docId":"Rust/section4/panic"},{"type":"link","label":"Error handling with Result & Option","href":"/docs/Rust/section4/error-handling","docId":"Rust/section4/error-handling"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section4/"},{"type":"category","label":"Intro to Intermediate Rust: Data Structs & Collections","items":[{"type":"link","label":"Collections - Vectors, Strings, Hashmaps","href":"/docs/Rust/section5/collections","docId":"Rust/section5/collections"},{"type":"link","label":"Data Structs","href":"/docs/Rust/section5/structs","docId":"Rust/section5/structs"},{"type":"link","label":"Defining Methods for Structs","href":"/docs/Rust/section5/struct-methods","docId":"Rust/section5/struct-methods"},{"type":"link","label":"Vectors, Strings, and &str Slices","href":"/docs/Rust/section5/vectors-vs-strings","docId":"Rust/section5/vectors-vs-strings"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section5/"},{"type":"category","label":"Intro to Advanced Rust: Traits, Generics, & Lifetimes","items":[{"type":"link","label":"Traits","href":"/docs/Rust/section6/traits","docId":"Rust/section6/traits"},{"type":"link","label":"Generic Types","href":"/docs/Rust/section6/generics","docId":"Rust/section6/generics"},{"type":"link","label":"Associated Types vs Generic Types","href":"/docs/Rust/section6/associated-generics","docId":"Rust/section6/associated-generics"},{"type":"link","label":"Lifetimes","href":"/docs/Rust/section6/lifetimes","docId":"Rust/section6/lifetimes"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section6/"},{"type":"category","label":"Intro to Advanced Rust: Iterators & Closures","items":[{"type":"link","label":"Iterators","href":"/docs/Rust/section7/iterators","docId":"Rust/section7/iterators"},{"type":"link","label":"Closures","href":"/docs/Rust/section7/closures","docId":"Rust/section7/closures"},{"type":"link","label":"Macros in Rust","href":"/docs/Rust/section7/macros","docId":"Rust/section7/macros"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section7/"},{"type":"category","label":"Learning Cargo, Rust\u2019s Package Management\\t System, Basic Testing","items":[{"type":"link","label":"Understanding Cargo","href":"/docs/Rust/section8/defining-cargo-config","docId":"Rust/section8/defining-cargo-config"},{"type":"link","label":"Installing a crate","href":"/docs/Rust/section8/installing-crate","docId":"Rust/section8/installing-crate"},{"type":"link","label":"Specifying features in crates","href":"/docs/Rust/section8/defining-crate-features","docId":"Rust/section8/defining-crate-features"},{"type":"link","label":"Unit Testing","href":"/docs/Rust/section8/unit-tests","docId":"Rust/section8/unit-tests"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section8/"},{"type":"link","label":"Learn more","href":"https://www.rust-lang.org/"}],"substrate":[{"type":"link","label":"Course Syllabus","href":"/docs/introsubstrate","docId":"introsubstrate"},{"type":"category","label":"Introduction to Substrate","items":[{"type":"link","label":"What is Substrate and FRAME?","href":"/docs/Substrate/section1/what-is-substrate","docId":"Substrate/section1/what-is-substrate"},{"type":"link","label":"History Behind Substrate","href":"/docs/Substrate/section1/substrate-history","docId":"Substrate/section1/substrate-history"},{"type":"link","label":"Substrate\u2019s Design Choices","href":"/docs/Substrate/section1/substrate-design","docId":"Substrate/section1/substrate-design"},{"type":"link","label":"Course Capstone Project","href":"/docs/Substrate/section1/capstone-project","docId":"Substrate/section1/capstone-project"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section1/"},{"type":"category","label":"Substrate Development 101","items":[{"type":"link","label":"Runtime & Outer Node","href":"/docs/Substrate/section2/substrate-runtime","docId":"Substrate/section2/substrate-runtime"},{"type":"link","label":"Storage","href":"/docs/Substrate/section2/substrate-storage","docId":"Substrate/section2/substrate-storage"},{"type":"link","label":"Pallets","href":"/docs/Substrate/section2/substrate-pallets","docId":"Substrate/section2/substrate-pallets"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section2/"},{"type":"category","label":"Installing Dependencies & Node Template","items":[{"type":"link","label":"Install, configure, and run the Node Template","href":"/docs/Substrate/section3/install-deps","docId":"Substrate/section3/install-deps"},{"type":"link","label":"Node Template Tour & Overview","href":"/docs/Substrate/section3/node-template-tour","docId":"Substrate/section3/node-template-tour"},{"type":"link","label":"Exploring the pallet template","href":"/docs/Substrate/section3/explore-pallet-template","docId":"Substrate/section3/explore-pallet-template"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section3/"},{"type":"category","label":"Building a Custom FRAME Pallet: Intro","items":[{"type":"link","label":"Adjusting Pallet Config & Runtime Overview","href":"/docs/Substrate/section4/pallet-config","docId":"Substrate/section4/pallet-config"},{"type":"link","label":"lib.rs Structure Deepdive","href":"/docs/Substrate/section4/project-structure","docId":"Substrate/section4/project-structure"},{"type":"link","label":"Creating storage maps","href":"/docs/Substrate/section4/create-storage-map","docId":"Substrate/section4/create-storage-map"},{"type":"link","label":"Events & Errors","href":"/docs/Substrate/section4/events-errors","docId":"Substrate/section4/events-errors"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section4/"},{"type":"category","label":"Building a Custom FRAME Pallet: Making our pallet actionable","items":[{"type":"link","label":"Creating dispatchable functions","href":"/docs/Substrate/section5/dispatchable","docId":"Substrate/section5/dispatchable"},{"type":"link","label":"Writing unit tests for pallets","href":"/docs/Substrate/section5/unit-tests","docId":"Substrate/section5/unit-tests"},{"type":"link","label":"Using other pallets","href":"/docs/Substrate/section5/coupling-pallets","docId":"Substrate/section5/coupling-pallets"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section5/"},{"type":"category","label":"Building a Custom FRAME Pallet: Deploying & Testing","items":[{"type":"link","label":"Running the node template","href":"/docs/Substrate/section6/run-node","docId":"Substrate/section6/run-node"},{"type":"link","label":"Use the frontend-template to test your pallet","href":"/docs/Substrate/section6/test-frontend","docId":"Substrate/section6/test-frontend"},{"type":"link","label":"Using Polkadot.js to explore your storage","href":"/docs/Substrate/section6/use-polkadotjs","docId":"Substrate/section6/use-polkadotjs"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section6/"},{"type":"category","label":"Building a Custom FRAME Pallet: Pallet & FRAME best practices","items":[{"type":"link","label":"Thinking in terms of blockchain development","href":"/docs/Substrate/section7/blockchain-dev","docId":"Substrate/section7/blockchain-dev"},{"type":"link","label":"Runtime Panics & FRAME Best Practices","href":"/docs/Substrate/section7/runtime-panics","docId":"Substrate/section7/runtime-panics"},{"type":"link","label":"How to approach testing in FRAME","href":"/docs/Substrate/section7/how-to-test-frame","docId":"Substrate/section7/how-to-test-frame"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section7/"},{"type":"category","label":"FRAME Deepdive","items":[{"type":"link","label":"Origins (Privileged Calls) in FRAME","href":"/docs/Substrate/section8/origins-calls","docId":"Substrate/section8/origins-calls"},{"type":"link","label":"Pallet Coupling","href":"/docs/Substrate/section8/pallet-coupling","docId":"Substrate/section8/pallet-coupling"},{"type":"link","label":"Chain Specification","href":"/docs/Substrate/section8/chain-genesis-spec","docId":"Substrate/section8/chain-genesis-spec"},{"type":"link","label":"Introduction to Benchmarking","href":"/docs/Substrate/section8/benchmarking","docId":"Substrate/section8/benchmarking"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section8/"},{"type":"link","label":"Learn more","href":"https://docs.substrate.io/"}],"web3":[{"type":"link","label":"Web3","href":"/docs/introweb3","docId":"introweb3"},{"type":"link","label":"Learn more","href":"https://web3.foundation/"}],"parachain":[{"type":"link","label":"Course Syllabus","href":"/docs/introparachain","docId":"introparachain"},{"type":"category","label":"Parachain Architecture Overview","items":[{"type":"link","label":"Polkadot Network Components","href":"/docs/Parachain/beginner/section1/network-components","docId":"Parachain/beginner/section1/network-components"},{"type":"link","label":"Relay Chain Architecture Overview","href":"/docs/Parachain/beginner/section1/relay-chain","docId":"Parachain/beginner/section1/relay-chain"},{"type":"link","label":"Parachain Architecture Overview","href":"/docs/Parachain/beginner/section1/parachain","docId":"Parachain/beginner/section1/parachain"}],"collapsed":true,"collapsible":true,"href":"/docs/Parachain/beginner/section1/"},{"type":"category","label":"Dependency Installation","items":[{"type":"link","label":"Install Local Binaries","href":"/docs/Parachain/beginner/section2/install-binary","docId":"Parachain/beginner/section2/install-binary"},{"type":"link","label":"Install the Cumulus Parachain Template","href":"/docs/Parachain/beginner/section2/install-template","docId":"Parachain/beginner/section2/install-template"},{"type":"link","label":"Running the Relay and Parachain","href":"/docs/Parachain/beginner/section2/running-chains","docId":"Parachain/beginner/section2/running-chains"}],"collapsed":true,"collapsible":true,"href":"/docs/Parachain/beginner/section2/"},{"type":"category","label":"Creating & Registering Parachain","items":[{"type":"link","label":"Creating a fast-tracked auction","href":"/docs/Parachain/beginner/section3/creating-auction","docId":"Parachain/beginner/section3/creating-auction"},{"type":"link","label":"Reserve your parathread","href":"/docs/Parachain/beginner/section3/creating-parathread","docId":"Parachain/beginner/section3/creating-parathread"},{"type":"link","label":"Developing your parachain with FRAME","href":"/docs/Parachain/beginner/section3/developing-parachain","docId":"Parachain/beginner/section3/developing-parachain"}],"collapsed":true,"collapsible":true,"href":"/docs/Parachain/beginner/section3/"},{"type":"category","label":"Moving Forward","items":[{"type":"link","label":"Ecosystem Initiatives","href":"/docs/Parachain/beginner/section4/initatives","docId":"Parachain/beginner/section4/initatives"},{"type":"link","label":"Road to Production","href":"/docs/Parachain/beginner/section4/road-to-production","docId":"Parachain/beginner/section4/road-to-production"}],"collapsed":true,"collapsible":true,"href":"/docs/Parachain/beginner/section4/"}],"atoz":[{"type":"link","label":"A to Z ELI5 Series","href":"/docs/introatoz","docId":"introatoz"},{"type":"link","label":"A for Account","href":"/docs/AtoZ/account","docId":"AtoZ/account"},{"type":"link","label":"B for Bridge","href":"/docs/AtoZ/bridge","docId":"AtoZ/bridge"},{"type":"link","label":"C for Collator","href":"/docs/AtoZ/collator","docId":"AtoZ/collator"},{"type":"link","label":"D for Democracy","href":"/docs/AtoZ/democracy","docId":"AtoZ/democracy"},{"type":"link","label":"E for Existential Deposit","href":"/docs/AtoZ/existential-deposit","docId":"AtoZ/existential-deposit"},{"type":"link","label":"F for Forkless","href":"/docs/AtoZ/forkless","docId":"AtoZ/forkless"},{"type":"link","label":"G for Grandpa","href":"/docs/AtoZ/grandpa","docId":"AtoZ/grandpa"},{"type":"link","label":"H for Hash","href":"/docs/AtoZ/hash","docId":"AtoZ/hash"},{"type":"link","label":"I for Interoperability","href":"/docs/AtoZ/interoperability","docId":"AtoZ/interoperability"},{"type":"link","label":"J for Polkadot JS","href":"/docs/AtoZ/polkadot-js","docId":"AtoZ/polkadot-js"},{"type":"link","label":"K for Kusama","href":"/docs/AtoZ/kusama","docId":"AtoZ/kusama"},{"type":"link","label":"L for Polkadot Launch","href":"/docs/AtoZ/launch","docId":"AtoZ/launch"},{"type":"link","label":"M for Multisig","href":"/docs/AtoZ/multisig","docId":"AtoZ/multisig"},{"type":"link","label":"N for Nominated Proof of Stake","href":"/docs/AtoZ/npos","docId":"AtoZ/npos"},{"type":"link","label":"O for On-chain Governance","href":"/docs/AtoZ/on-chain-governance","docId":"AtoZ/on-chain-governance"},{"type":"link","label":"P for Phragm\xe9n","href":"/docs/AtoZ/phragm\xe9n","docId":"AtoZ/phragm\xe9n"},{"type":"link","label":"Q for FAQ","href":"/docs/AtoZ/q-faq","docId":"AtoZ/q-faq"}]},"docs":{"AtoZ/account":{"id":"AtoZ/account","title":"A for Account","description":"A for Account","sidebar":"atoz"},"AtoZ/bridge":{"id":"AtoZ/bridge","title":"B for Bridge","description":"B for Bridges","sidebar":"atoz"},"AtoZ/collator":{"id":"AtoZ/collator","title":"C for Collator","description":"C for Collators","sidebar":"atoz"},"AtoZ/democracy":{"id":"AtoZ/democracy","title":"D for Democracy","description":"D for Democracy","sidebar":"atoz"},"AtoZ/existential-deposit":{"id":"AtoZ/existential-deposit","title":"E for Existential Deposit","description":"E for Existential Deposit","sidebar":"atoz"},"AtoZ/forkless":{"id":"AtoZ/forkless","title":"F for Forkless","description":"F for Forkless","sidebar":"atoz"},"AtoZ/grandpa":{"id":"AtoZ/grandpa","title":"G for GRANDPA","description":"G for GRANDPA","sidebar":"atoz"},"AtoZ/hash":{"id":"AtoZ/hash","title":"H for Hash","description":"H for Hash","sidebar":"atoz"},"AtoZ/interoperability":{"id":"AtoZ/interoperability","title":"I for Interoperability","description":"I for Interoperability","sidebar":"atoz"},"AtoZ/kusama":{"id":"AtoZ/kusama","title":"K for Kusama","description":"K for Kusama","sidebar":"atoz"},"AtoZ/launch":{"id":"AtoZ/launch","title":"L for Polkadot Launch","description":"L for Polkadot Launch","sidebar":"atoz"},"AtoZ/multisig":{"id":"AtoZ/multisig","title":"M for Multisig","description":"M for Multisig","sidebar":"atoz"},"AtoZ/npos":{"id":"AtoZ/npos","title":"N for Nominated Proof of Stake (NPoS)","description":"N for Nominated Proof of Stake (NPoS)","sidebar":"atoz"},"AtoZ/on-chain-governance":{"id":"AtoZ/on-chain-governance","title":"O for On-chain Governance","description":"O for On-chain Governance","sidebar":"atoz"},"AtoZ/phragm\xe9n":{"id":"AtoZ/phragm\xe9n","title":"P for Phragm\xe9n","description":"P for Phragm\xe9n","sidebar":"atoz"},"AtoZ/polkadot-js":{"id":"AtoZ/polkadot-js","title":"J for Polkadot JS","description":"J for Polkadot JS","sidebar":"atoz"},"AtoZ/q-faq":{"id":"AtoZ/q-faq","title":"Q for FAQ","description":"J for Polkadot JS","sidebar":"atoz"},"Blockchain/Module1/bitcoin":{"id":"Blockchain/Module1/bitcoin","title":"Introduction to Bitcoin","description":"","sidebar":"blockchain"},"Blockchain/Module1/blockchain":{"id":"Blockchain/Module1/blockchain","title":"What is a Blockchain?","description":"- A data structure","sidebar":"blockchain"},"Blockchain/Module1/digital-money":{"id":"Blockchain/Module1/digital-money","title":"Dawn of Digital Money","description":"","sidebar":"blockchain"},"Blockchain/Module1/ethereum":{"id":"Blockchain/Module1/ethereum","title":"Introduction to Ethereum","description":"","sidebar":"blockchain"},"Blockchain/Module1/money-properties":{"id":"Blockchain/Module1/money-properties","title":"Properties of Money","description":"","sidebar":"blockchain"},"Blockchain/Module1/money-trust":{"id":"Blockchain/Module1/money-trust","title":"History of Money and Trust","description":"","sidebar":"blockchain"},"Blockchain/Module1/web3":{"id":"Blockchain/Module1/web3","title":"Introduction to Web3","description":"","sidebar":"blockchain"},"Blockchain/Module2/cryptography":{"id":"Blockchain/Module2/cryptography","title":"Cryptography and Blockchain","description":"","sidebar":"blockchain"},"Blockchain/Module2/datastructures":{"id":"Blockchain/Module2/datastructures","title":"Blockchain Datastructures","description":"","sidebar":"blockchain"},"Blockchain/Module2/decentralization":{"id":"Blockchain/Module2/decentralization","title":"Decentralization Trilemma","description":"","sidebar":"blockchain"},"Blockchain/Module2/hash":{"id":"Blockchain/Module2/hash","title":"Cryptographic Hashing","description":"","sidebar":"blockchain"},"Blockchain/Module2/invincible":{"id":"Blockchain/Module2/invincible","title":"How Invincible is Bitcoin Network?","description":"","sidebar":"blockchain"},"Blockchain/Module2/keypair":{"id":"Blockchain/Module2/keypair","title":"Digital Signatures and Crypto-wallets","description":"","sidebar":"blockchain"},"Blockchain/Module2/trust":{"id":"Blockchain/Module2/trust","title":"Trust issues","description":"","sidebar":"blockchain"},"Blockchain/Module3/finality":{"id":"Blockchain/Module3/finality","title":"Block finality","description":"","sidebar":"blockchain"},"Blockchain/Module3/forking":{"id":"Blockchain/Module3/forking","title":"Hardforks of Blockchain","description":"","sidebar":"blockchain"},"Blockchain/Module3/hashpower":{"id":"Blockchain/Module3/hashpower","title":"Bitcoin Hashpower","description":"","sidebar":"blockchain"},"Blockchain/Module3/mining":{"id":"Blockchain/Module3/mining","title":"How Bitcoin blocks are mined?","description":"","sidebar":"blockchain"},"Blockchain/Module3/mining-hardware":{"id":"Blockchain/Module3/mining-hardware","title":"Bitcoin mining hardware","description":"","sidebar":"blockchain"},"Blockchain/Module3/PoS":{"id":"Blockchain/Module3/PoS","title":"Proof of Staking","description":"","sidebar":"blockchain"},"Blockchain/Module3/PoW":{"id":"Blockchain/Module3/PoW","title":"Proof of Work","description":"","sidebar":"blockchain"},"Blockchain/Module4/consensus":{"id":"Blockchain/Module4/consensus","title":"Network Consensus","description":"","sidebar":"blockchain"},"Blockchain/Module4/distributed":{"id":"Blockchain/Module4/distributed","title":"Introduction to Distributed Systems","description":"","sidebar":"blockchain"},"Blockchain/Module4/gossip":{"id":"Blockchain/Module4/gossip","title":"How do Blockchain nodes communicate?","description":"","sidebar":"blockchain"},"Blockchain/Module4/lightclients":{"id":"Blockchain/Module4/lightclients","title":"Light clients and Trustless Networks","description":"","sidebar":"blockchain"},"Blockchain/Module4/network-challenges":{"id":"Blockchain/Module4/network-challenges","title":"Networking Challenges and Opportunities","description":"","sidebar":"blockchain"},"Blockchain/Module4/networkstack":{"id":"Blockchain/Module4/networkstack","title":"Network Stack","description":"","sidebar":"blockchain"},"Blockchain/Module4/nodes":{"id":"Blockchain/Module4/nodes","title":"Types of Network Nodes","description":"","sidebar":"blockchain"},"Blockchain/Module5/enterprise":{"id":"Blockchain/Module5/enterprise","title":"Enterprise Blockchains","description":"","sidebar":"blockchain"},"Blockchain/Module5/layer0":{"id":"Blockchain/Module5/layer0","title":"Layer 0 Blockchains","description":"","sidebar":"blockchain"},"Blockchain/Module5/layer1":{"id":"Blockchain/Module5/layer1","title":"Layer 1 Blockchains","description":"","sidebar":"blockchain"},"Blockchain/Module5/layer2":{"id":"Blockchain/Module5/layer2","title":"Layer 2 Blockchains","description":"","sidebar":"blockchain"},"Blockchain/Module5/layers":{"id":"Blockchain/Module5/layers","title":"What are Blockchain Layers?","description":"","sidebar":"blockchain"},"Blockchain/Module5/zk-proofs":{"id":"Blockchain/Module5/zk-proofs","title":"ZK Proofs and Applications","description":"","sidebar":"blockchain"},"Blockchain/Module6/computing":{"id":"Blockchain/Module6/computing","title":"Decentralized Computing","description":"","sidebar":"blockchain"},"Blockchain/Module6/crypto-defi":{"id":"Blockchain/Module6/crypto-defi","title":"Cryptocurrencies and DeFi","description":"","sidebar":"blockchain"},"Blockchain/Module6/dao":{"id":"Blockchain/Module6/dao","title":"DAOs and Governance","description":"","sidebar":"blockchain"},"Blockchain/Module6/future-web3":{"id":"Blockchain/Module6/future-web3","title":"Future of Web3","description":"","sidebar":"blockchain"},"Blockchain/Module6/nft-meta":{"id":"Blockchain/Module6/nft-meta","title":"NFTs and Metaverse","description":"","sidebar":"blockchain"},"Blockchain/Module6/web3":{"id":"Blockchain/Module6/web3","title":"Introduction to Web3?","description":"","sidebar":"blockchain"},"intro":{"id":"intro","title":"Web3 Education Initiative","description":"Four courses - Blockchain Fundamentals, Introduction to Polkadot, Introduction to Rust Programming and Introduction to Substrate"},"introatoz":{"id":"introatoz","title":"W3F A - Z ELI5 series","description":"ELI5 - Explain like I am five!","sidebar":"atoz"},"introblock":{"id":"introblock","title":"Blockchain Basics","description":"Developed by the Technical Education team at the Web3 Foundation, this course provides a beginner\'s introduction to Blockchain technology.","sidebar":"blockchain"},"introdot":{"id":"introdot","title":"Introduction to Polkadot","description":"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 \u201cWhat is Polkadot?\u201d. 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.","sidebar":"polkadot"},"introparachain":{"id":"introparachain","title":"Parachain Development Guide","description":"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.","sidebar":"parachain"},"introrust":{"id":"introrust","title":"Introduction to Rust for Blockchain Development","description":"Developed by the Technical Education team at the Web3 Foundation, this course introduces programming in Rust for Blockchain applications.","sidebar":"rust"},"introsubstrate":{"id":"introsubstrate","title":"Introduction to Substrate & FRAME","description":"Developed by the Technical Education team at the Web3 Foundation, this course provides a comprehensive overview of the Substrate blockchain framework and building blockchains.","sidebar":"substrate"},"introweb3":{"id":"introweb3","title":"Introduction to Web3","description":"Developed by the Technical Education team at the Web3 Foundation, this course provides a beginner\'s introduction to Web3. Less trust, more truth.","sidebar":"web3"},"Parachain/beginner/section1/index":{"id":"Parachain/beginner/section1/index","title":"Parachain Architecture Overview","description":"","sidebar":"parachain"},"Parachain/beginner/section1/network-components":{"id":"Parachain/beginner/section1/network-components","title":"Polkadot Network Components","description":"Get a brief overview of how the Polkadot network operates and crucial components.","sidebar":"parachain"},"Parachain/beginner/section1/parachain":{"id":"Parachain/beginner/section1/parachain","title":"Parachain Architecture Overview","description":"Learn the structure of a parachain, what tools are used to develop it, and how it relates to the relay chain.","sidebar":"parachain"},"Parachain/beginner/section1/relay-chain":{"id":"Parachain/beginner/section1/relay-chain","title":"Relay Chain Architecture Overview","description":"Learn the role of the relay chain, and how it registers and validates parachains.","sidebar":"parachain"},"Parachain/beginner/section2/index":{"id":"Parachain/beginner/section2/index","title":"Dependency Installation","description":"","sidebar":"parachain"},"Parachain/beginner/section2/install-binary":{"id":"Parachain/beginner/section2/install-binary","title":"Install Local Binaries","description":"Install the necessary Polkadot binaries onto your local machine.","sidebar":"parachain"},"Parachain/beginner/section2/install-template":{"id":"Parachain/beginner/section2/install-template","title":"Install the Cumulus Parachain Template","description":"Install the Cumulus Parachain Template","sidebar":"parachain"},"Parachain/beginner/section2/running-chains":{"id":"Parachain/beginner/section2/running-chains","title":"Running the Relay and Parachain","description":"Ensure both the relay chain and parachain run locally.","sidebar":"parachain"},"Parachain/beginner/section3/creating-auction":{"id":"Parachain/beginner/section3/creating-auction","title":"Creating a fast-tracked auction","description":"Creating and registering your parathread via auction.","sidebar":"parachain"},"Parachain/beginner/section3/creating-parathread":{"id":"Parachain/beginner/section3/creating-parathread","title":"Reserve your parathread","description":"Reserve your parathread and para ID.","sidebar":"parachain"},"Parachain/beginner/section3/developing-parachain":{"id":"Parachain/beginner/section3/developing-parachain","title":"Developing your parachain with FRAME","description":"Configure and customize your parachain before registering it on the relay chain.","sidebar":"parachain"},"Parachain/beginner/section3/index":{"id":"Parachain/beginner/section3/index","title":"Creating & Registering Parachain","description":"","sidebar":"parachain"},"Parachain/beginner/section4/index":{"id":"Parachain/beginner/section4/index","title":"Moving Forward","description":"","sidebar":"parachain"},"Parachain/beginner/section4/initatives":{"id":"Parachain/beginner/section4/initatives","title":"Ecosystem Initiatives","description":"Explore different avenues for expanding your project within the Polkadot ecosystem.","sidebar":"parachain"},"Parachain/beginner/section4/road-to-production":{"id":"Parachain/beginner/section4/road-to-production","title":"Road to Production","description":"Learn the next technical steps to put your parachain to the road of production.","sidebar":"parachain"},"Polkadot/Module1/architecture":{"id":"Polkadot/Module1/architecture","title":"Architecture of Polkadot","description":"Polkadot utilises a central chain called the relay chain which communicates with mul-","sidebar":"polkadot"},"Polkadot/Module1/becomeparachain":{"id":"Polkadot/Module1/becomeparachain","title":"Becoming a Parachain on Polkadot","description":"The Polkadot system consists of a single open collaborative decentralised network called the","sidebar":"polkadot"},"Polkadot/Module1/features":{"id":"Polkadot/Module1/features","title":"Features of Polkadot","description":"Bitcoin and Ethereumare proof-of-work (PoW) blockchains where security relies","sidebar":"polkadot"},"Polkadot/Module1/polkadot":{"id":"Polkadot/Module1/polkadot","title":"What is Polkadot?","description":"Polkadot is a heterogenous multi-chain protocol. Polkadot addresses some of the existing shortcomings","sidebar":"polkadot"},"Polkadot/Module1/polkadotvision":{"id":"Polkadot/Module1/polkadotvision","title":"Vision of Polkadot","description":"The Internet was originally designed for and built upon decentralised protocols such as TCP/IP,","sidebar":"polkadot"},"Polkadot/Module2/account":{"id":"Polkadot/Module2/account","title":"Create an Account","description":"","sidebar":"polkadot"},"Polkadot/Module2/dotutility":{"id":"Polkadot/Module2/dotutility","title":"DOT Token Utility","description":"On the side of economics, we aim to have a controlled near-constant yearly inflation","sidebar":"polkadot"},"Polkadot/Module2/explorenetwork":{"id":"Polkadot/Module2/explorenetwork","title":"Exploring Polkadot Network","description":"Polkadot as a State Machine","sidebar":"polkadot"},"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","sidebar":"polkadot"},"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","sidebar":"polkadot"},"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","sidebar":"polkadot"},"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","sidebar":"polkadot"},"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","sidebar":"polkadot"},"Polkadot/Module3/securityimprovements":{"id":"Polkadot/Module3/securityimprovements","title":"Security and Consensus Improvements","description":"Sassafras","sidebar":"polkadot"},"Polkadot/Module3/sharedsecurity":{"id":"Polkadot/Module3/sharedsecurity","title":"Securing the Network","description":"Polkadot assumes that parachains are running as external untrusted clients of the relay chain and that","sidebar":"polkadot"},"Polkadot/Module4/cryptography":{"id":"Polkadot/Module4/cryptography","title":"Cryptography","description":"We assume that malicious parties generate their keys with an arbitrary algorithm while","sidebar":"polkadot"},"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 -","sidebar":"polkadot"},"Polkadot/Module4/networking":{"id":"Polkadot/Module4/networking","title":"Networking","description":"All validators have their own local clock and their clocks do","sidebar":"polkadot"},"Polkadot/Module4/nodes":{"id":"Polkadot/Module4/nodes","title":"Nodes on Polkadot Network","description":"The Polkadot relay chain network consists of nodes and roles. Nodes are the network-level enti-","sidebar":"polkadot"},"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","sidebar":"polkadot"},"Polkadot/Module5/architectureimprovements":{"id":"Polkadot/Module5/architectureimprovements","title":"Polkadot Architecture Improvements","description":"XCMP","sidebar":"polkadot"},"Polkadot/Module5/bridges":{"id":"Polkadot/Module5/bridges","title":"Polkadot Bridges","description":"Polkadot-Kusama Bridge","sidebar":"polkadot"},"Polkadot/Module5/interoperability":{"id":"Polkadot/Module5/interoperability","title":"Interoperability","description":"XCMP","sidebar":"polkadot"},"Polkadot/Module5/parachains":{"id":"Polkadot/Module5/parachains","title":"Parachains","description":"","sidebar":"polkadot"},"Polkadot/Module5/scalability":{"id":"Polkadot/Module5/scalability","title":"Scalability","description":"Validity and Availability","sidebar":"polkadot"},"Polkadot/Module6/developers":{"id":"Polkadot/Module6/developers","title":"Developer Community","description":"Community Contributions","sidebar":"polkadot"},"Polkadot/Module6/polkadotjs":{"id":"Polkadot/Module6/polkadotjs","title":"Polkadot JS","description":"","sidebar":"polkadot"},"Polkadot/Module6/rust":{"id":"Polkadot/Module6/rust","title":"Rust for Blockchain Development","description":"","sidebar":"polkadot"},"Polkadot/Module6/substrate":{"id":"Polkadot/Module6/substrate","title":"Introduction to Substrate","description":"Substrate - A Blockchain Building Framework","sidebar":"polkadot"},"Polkadot/Module6/testnets":{"id":"Polkadot/Module6/testnets","title":"Polkadot Test Networks","description":"","sidebar":"polkadot"},"polkadotFAQ":{"id":"polkadotFAQ","title":"Polkadot FAQ","description":"Where can I find a list of academic and in-depth articles and information about Polkadot?"},"Rust/rust-appendix":{"id":"Rust/rust-appendix","title":"Course Appendix","description":"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.","sidebar":"rust"},"Rust/section1/index":{"id":"Rust/section1/index","title":"Why Learn Rust?","description":"Welcome to the Introduction to Rust course by Web3 Foundation. This introductory course focuses on bringing the most impactful components of the Rust Programming Language to light and it will focus on the key concepts to start your Web3 learning journey.","sidebar":"rust"},"Rust/section1/wasm-tldr":{"id":"Rust/section1/wasm-tldr","title":"WASM TLDR - What is WASM, and why is it important?","description":"Why WASM is used, and it\'s support in Rust.","sidebar":"rust"},"Rust/section1/what-is-rust":{"id":"Rust/section1/what-is-rust","title":"What is Rust?","description":"An introduction to what the Rust programming language is, and how it compares to other languages.","sidebar":"rust"},"Rust/section1/why-rust":{"id":"Rust/section1/why-rust","title":"Why Learn Rust?","description":"A brief overview of the pros of learning the Rust programming language.","sidebar":"rust"},"Rust/section2/data-types":{"id":"Rust/section2/data-types","title":"Data Types in Rust","description":"Learn what data types are and which basic ones exist.","sidebar":"rust"},"Rust/section2/functions-comments":{"id":"Rust/section2/functions-comments","title":"Functions & Comments in Rust","description":"Learn how functions and comments work, and how to create them in Rust.","sidebar":"rust"},"Rust/section2/heap-vs-stack":{"id":"Rust/section2/heap-vs-stack","title":"The Heap vs. The Stack","description":"Learn the summarized differences between the stack and the heap and how they affect your Rust programming.","sidebar":"rust"},"Rust/section2/index":{"id":"Rust/section2/index","title":"Rust 101 - Intro to Basic Rust","description":"The second module will dive right into the very basics of Rust. Learn how variables, scope, and mutability work together, along with basic data types and how Rust can create functions. Lastly, learn how to perform iterative operations with loops.","sidebar":"rust"},"Rust/section2/loops":{"id":"Rust/section2/loops","title":"Loops & Basic Logic Flows","description":"Learn how to construct loops, if statements, and when to use the two.","sidebar":"rust"},"Rust/section2/module2":{"id":"Rust/section2/module2","title":"2. Intro to Basic Rust","description":"Variable Scope. Memory management. How variables interact. References. Background on Programming Safety and why it is critical for Blockchain development. Substrate introduction."},"Rust/section2/variables-mutability":{"id":"Rust/section2/variables-mutability","title":"Variables & Mutability","description":"How variables and mutability works in Rust.","sidebar":"rust"},"Rust/section3/borrowing":{"id":"Rust/section3/borrowing","title":"Borrowing & References in Rust","description":"Learn how the borrowing model works in Rust, and how to utilize it properly.","sidebar":"rust"},"Rust/section3/index":{"id":"Rust/section3/index","title":"Intro to Intermediate Rust - Ownership, Borrowing, & Slices","description":"This module will focus on more intermediate concepts relating to Rust\'s ownership and borrowing model.","sidebar":"rust"},"Rust/section3/module3":{"id":"Rust/section3/module3","title":"3. Intro to Intermediate Rust","description":"Structs. Enums. Methods. Packages and Crates."},"Rust/section3/ownership":{"id":"Rust/section3/ownership","title":"Rust\'s Ownership Model","description":"Learn how the ownership model works in Rust, and why it allows for Rust to be safe at compile time.","sidebar":"rust"},"Rust/section3/slices":{"id":"Rust/section3/slices","title":"Slices in Rust","description":"Learn what slices are, how they work, and why they are useful.","sidebar":"rust"},"Rust/section4/enums":{"id":"Rust/section4/enums","title":"Enums in Rust","description":"Learn what enums are and how to use them in Rust.","sidebar":"rust"},"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.","sidebar":"rust"},"Rust/section4/index":{"id":"Rust/section4/index","title":"Intro to Intermediate Rust - Enums & Matching Patterns","description":"In this module, you will become familiar with some of Rust\'s powerful pattern matching and enumeration capabilities.","sidebar":"rust"},"Rust/section4/panic":{"id":"Rust/section4/panic","title":"Panic! in Rust","description":"Discover what panic! in Rust is, and when you should panic.","sidebar":"rust"},"Rust/section5/collections":{"id":"Rust/section5/collections","title":"Collections in Rust - Vectors, Strings, Hashmaps","description":"Learn what collections are, and when to use which in Rust.","sidebar":"rust"},"Rust/section5/index":{"id":"Rust/section5/index","title":"Intro to Intermediate Rust - Data Structs & Collections","description":"This module will delve into some more intermediate concepts: collections and data structs.","sidebar":"rust"},"Rust/section5/struct-methods":{"id":"Rust/section5/struct-methods","title":"Defining Methods for Structs","description":"Learn how to define methods for structs in Rust.","sidebar":"rust"},"Rust/section5/structs":{"id":"Rust/section5/structs","title":"Data Structs in rust","description":"Learn what data structs are in Rust.","sidebar":"rust"},"Rust/section5/vectors-vs-strings":{"id":"Rust/section5/vectors-vs-strings","title":"Vectors, Strings, and &str Slices","description":"Learn the difference between vectors and strings in Rust.","sidebar":"rust"},"Rust/section6/associated-generics":{"id":"Rust/section6/associated-generics","title":"Associated Types vs Generic Types","description":"Learn the difference between associated types vs generic types.","sidebar":"rust"},"Rust/section6/generics":{"id":"Rust/section6/generics","title":"Generic Types in Rust","description":"Learn what generic types are and how to use them in Rust.","sidebar":"rust"},"Rust/section6/index":{"id":"Rust/section6/index","title":"Intro to Advanced Rust - Traits, Generics, & Lifetimes","description":"The following module deals with the concepts of traits, generics, and an overall deeper look at the robust type system that Rust provides. Lifetimes, yet another safety mechanism to ensure that references are living correctly within the program.","sidebar":"rust"},"Rust/section6/lifetimes":{"id":"Rust/section6/lifetimes","title":"Lifetimes in Rust","description":"Learn the Rust lifetime model.","sidebar":"rust"},"Rust/section6/traits":{"id":"Rust/section6/traits","title":"Defining behavior with Traits","description":"Learn how to bring data structs to life with traits in Rust.","sidebar":"rust"},"Rust/section7/closures":{"id":"Rust/section7/closures","title":"Closure Functions in Rust","description":"Learn how to use function closures.","sidebar":"rust"},"Rust/section7/index":{"id":"Rust/section7/index","title":"Intro to Advanced Rust - Iterators & Closures","description":"Delving deeper into more of the Rust Standard Library\'s extended functionality and more advanced concepts: iterators and closures.","sidebar":"rust"},"Rust/section7/iterators":{"id":"Rust/section7/iterators","title":"Iterators in Rust","description":"Learn how to use iterators in Rust.","sidebar":"rust"},"Rust/section7/macros":{"id":"Rust/section7/macros","title":"Macros in Rust","description":"Learn what macros are and how to create basic macros in Rust.","sidebar":"rust"},"Rust/section8/defining-cargo-config":{"id":"Rust/section8/defining-cargo-config","title":"Reading & Defining `cargo.toml`","description":"Understand how to utilize cargo.","sidebar":"rust"},"Rust/section8/defining-crate-features":{"id":"Rust/section8/defining-crate-features","title":"Features in Cargo","description":"Specify features for crates to utilize.","sidebar":"rust"},"Rust/section8/index":{"id":"Rust/section8/index","title":"Learning Cargo, Rust\u2019s Package Management System & Unit Testing","description":"This next module will focus on understanding the structure of a Rust project. It will be less in the playground environment we\'ve been using and more in a code editor like Visual Studio Code and the terminal.","sidebar":"rust"},"Rust/section8/installing-crate":{"id":"Rust/section8/installing-crate","title":"Installing a cargo crate","description":"Understand how to install a crate and use it in a project.","sidebar":"rust"},"Rust/section8/unit-tests":{"id":"Rust/section8/unit-tests","title":"Basic Unit Testing in Rust","description":"Understand how to unit test your Rust code.","sidebar":"rust"},"Rust/setup/installation":{"id":"Rust/setup/installation","title":"Installing & Setting up a Rust Developer Environment","description":"Installing Rust, and setting up your development environment.","sidebar":"rust"},"Substrate/section1/capstone-project":{"id":"Substrate/section1/capstone-project","title":"Course Capstone Project","description":"Learn the specification of what exactly you will build in this course.","sidebar":"substrate"},"Substrate/section1/index":{"id":"Substrate/section1/index","title":"Why Learn Substrate?","description":"","sidebar":"substrate"},"Substrate/section1/substrate-design":{"id":"Substrate/section1/substrate-design","title":"Substrate\u2019s Design Choices","description":"Learn how the design choices of Substrate were made, and why they matter.","sidebar":"substrate"},"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.","sidebar":"substrate"},"Substrate/section1/what-is-substrate":{"id":"Substrate/section1/what-is-substrate","title":"Substrate - powering Web3","description":"Learn what Substrate it is, and why it\'s the future of building infrastructure in web3.","sidebar":"substrate"},"Substrate/section2/index":{"id":"Substrate/section2/index","title":"Substrate 101 - Overview of the Substrate Framework","description":"","sidebar":"substrate"},"Substrate/section2/substrate-pallets":{"id":"Substrate/section2/substrate-pallets","title":"Pallets","description":"Learn what pallets are, and how they fit into FRAME.","sidebar":"substrate"},"Substrate/section2/substrate-runtime":{"id":"Substrate/section2/substrate-runtime","title":"Runtime & Outer Node","description":"Learn what the runtime in Substrate is, and how it defines a state transition function.","sidebar":"substrate"},"Substrate/section2/substrate-storage":{"id":"Substrate/section2/substrate-storage","title":"Storage","description":"Learn how storage works in Substrate, and how it is crucial for managing state.","sidebar":"substrate"},"Substrate/section3/explore-pallet-template":{"id":"Substrate/section3/explore-pallet-template","title":"Exploring the pallet template","description":"Explore the pallet template, and get a sneak peek into what is to come next!","sidebar":"substrate"},"Substrate/section3/index":{"id":"Substrate/section3/index","title":"Substrate Node Template","description":"","sidebar":"substrate"},"Substrate/section3/install-deps":{"id":"Substrate/section3/install-deps","title":"Install, configure, and run the Node Template","description":"Install the substrate-node-template, which will jump-start the development process!","sidebar":"substrate"},"Substrate/section3/install-explore-frontend":{"id":"Substrate/section3/install-explore-frontend","title":"Installing & exploring the frontend template","description":"Install and explore the frontend template for debugging and developing your pallet."},"Substrate/section3/node-template-tour":{"id":"Substrate/section3/node-template-tour","title":"Node Template Tour & Overview","description":"Get familiar with the Substrate node template and its various components.","sidebar":"substrate"},"Substrate/section4/create-storage-map":{"id":"Substrate/section4/create-storage-map","title":"Creating storage maps","description":"Define and create storage maps for our pallet.","sidebar":"substrate"},"Substrate/section4/events-errors":{"id":"Substrate/section4/events-errors","title":"Events & Errors","description":"Define the necessary events and errors needed for our pallet","sidebar":"substrate"},"Substrate/section4/index":{"id":"Substrate/section4/index","title":"Intro to Building a Custom FRAME Pallet","description":"","sidebar":"substrate"},"Substrate/section4/pallet-config":{"id":"Substrate/section4/pallet-config","title":"Adjusting Pallet Config & Runtime Overview","description":"Learn how a pallet\'s configuration works and connects with the runtime.","sidebar":"substrate"},"Substrate/section4/project-structure":{"id":"Substrate/section4/project-structure","title":"lib.rs Structure Deepdive","description":"Learn the structure of the pallet works, including how events, errors, and dispatchable functions fit together.","sidebar":"substrate"},"Substrate/section5/coupling-pallets":{"id":"Substrate/section5/coupling-pallets","title":"Using other pallets","description":"Learn how coupling other pallets can extend your own pallet\'s functionality","sidebar":"substrate"},"Substrate/section5/dispatchable":{"id":"Substrate/section5/dispatchable","title":"Creating dispatchable functions","description":"Creating dispatchable functions for our pallet.","sidebar":"substrate"},"Substrate/section5/index":{"id":"Substrate/section5/index","title":"Building a Custom FRAME Pallet - Making our pallet actionable","description":"With our events, errors, and storage in place within our pallet, we can now move on to making it actionable. This module will focus on dispatchable functions and including other pallets into ours.","sidebar":"substrate"},"Substrate/section5/unit-tests":{"id":"Substrate/section5/unit-tests","title":"Writing unit tests for pallets","description":"Learn how to unit test your pallet.","sidebar":"substrate"},"Substrate/section6/index":{"id":"Substrate/section6/index","title":"Building a Custom FRAME Pallet - Deploying & Testing","description":"With tests and functionality in place, you will now deploy and run your pallet in a real environment.","sidebar":"substrate"},"Substrate/section6/run-node":{"id":"Substrate/section6/run-node","title":"Running the node template","description":"Run and review the output from our custom pallet and node.","sidebar":"substrate"},"Substrate/section6/test-frontend":{"id":"Substrate/section6/test-frontend","title":"Use the frontend-template to test your pallet","description":"Testing our pallet using the frontend-template","sidebar":"substrate"},"Substrate/section6/use-polkadotjs":{"id":"Substrate/section6/use-polkadotjs","title":"Using Polkadot.js to explore your storage","description":"Use Polkadot.js to view events, errors, and storage relating to your node and pallet.","sidebar":"substrate"},"Substrate/section7/blockchain-dev":{"id":"Substrate/section7/blockchain-dev","title":"Thinking in terms of blockchain development","description":"How to think like a runtime developer and avoid common pitfalls.","sidebar":"substrate"},"Substrate/section7/how-to-test-frame":{"id":"Substrate/section7/how-to-test-frame","title":"How to approach testing in FRAME","description":"How to correctly approach testing in FRAME and a deeper look at how to mock types properly.","sidebar":"substrate"},"Substrate/section7/index":{"id":"Substrate/section7/index","title":"Building a Custom FRAME Pallet - Pallet & FRAME best practices","description":"In this module, you will learn more granular details and practices on ensuring your pallets are built using sound practices.","sidebar":"substrate"},"Substrate/section7/runtime-panics":{"id":"Substrate/section7/runtime-panics","title":"Runtime Panics & FRAME Best Practices","description":"Learn how to solidify safe programming practices using Rust error handling to avoid panicking in the runtime.","sidebar":"substrate"},"Substrate/section8/benchmarking":{"id":"Substrate/section8/benchmarking","title":"Introduction to Benchmarking","description":"A basic introduction to benchmarking in FRAME","sidebar":"substrate"},"Substrate/section8/chain-genesis-spec":{"id":"Substrate/section8/chain-genesis-spec","title":"Chain Specification","description":"Learn how basis chain and genesis configurations work in FRAME","sidebar":"substrate"},"Substrate/section8/index":{"id":"Substrate/section8/index","title":"FRAME Deepdive","description":"After learning about how to approach testing and development of a FRAME pallet, this module will touch on a few FRAME concepts.","sidebar":"substrate"},"Substrate/section8/origins-calls":{"id":"Substrate/section8/origins-calls","title":"Origins (Privileged Calls) in FRAME","description":"Learn how origins and calls work in Substrate and FRAME.","sidebar":"substrate"},"Substrate/section8/pallet-coupling":{"id":"Substrate/section8/pallet-coupling","title":"Pallet Coupling","description":"Learn how to couple pallets to each other, and the differences between tight and loose (and when to use which).","sidebar":"substrate"}}}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"blockchain":[{"type":"link","label":"Blockchain Fundamentals","href":"/docs/introblock","docId":"introblock"},{"type":"category","label":"Introduction to Blockchain","collapsed":false,"items":[{"type":"link","label":"What is a Blockchain?","href":"/docs/Blockchain/Module1/blockchain","docId":"Blockchain/Module1/blockchain"},{"type":"link","label":"History of Money and Trust","href":"/docs/Blockchain/Module1/money-trust","docId":"Blockchain/Module1/money-trust"},{"type":"link","label":"Properties of Money","href":"/docs/Blockchain/Module1/money-properties","docId":"Blockchain/Module1/money-properties"},{"type":"link","label":"Dawn of Digital Money","href":"/docs/Blockchain/Module1/digital-money","docId":"Blockchain/Module1/digital-money"},{"type":"link","label":"Introduction to Bitcoin","href":"/docs/Blockchain/Module1/bitcoin","docId":"Blockchain/Module1/bitcoin"},{"type":"link","label":"Introduction to Ethereum","href":"/docs/Blockchain/Module1/ethereum","docId":"Blockchain/Module1/ethereum"},{"type":"link","label":"Introduction to Web3","href":"/docs/Blockchain/Module1/web3","docId":"Blockchain/Module1/web3"}],"collapsible":true},{"type":"category","label":"Blockchain and Cryptography","items":[{"type":"link","label":"Cryptography and Blockchain","href":"/docs/Blockchain/Module2/cryptography","docId":"Blockchain/Module2/cryptography"},{"type":"link","label":"Digital Signatures and Crypto-wallets","href":"/docs/Blockchain/Module2/keypair","docId":"Blockchain/Module2/keypair"},{"type":"link","label":"Cryptographic Hashing","href":"/docs/Blockchain/Module2/hash","docId":"Blockchain/Module2/hash"},{"type":"link","label":"Blockchain Datastructures","href":"/docs/Blockchain/Module2/datastructures","docId":"Blockchain/Module2/datastructures"},{"type":"link","label":"Trust issues","href":"/docs/Blockchain/Module2/trust","docId":"Blockchain/Module2/trust"},{"type":"link","label":"How Invincible is Bitcoin Network?","href":"/docs/Blockchain/Module2/invincible","docId":"Blockchain/Module2/invincible"},{"type":"link","label":"Decentralization Trilemma","href":"/docs/Blockchain/Module2/decentralization","docId":"Blockchain/Module2/decentralization"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Block Mining and Finality","items":[{"type":"link","label":"How Bitcoin blocks are mined?","href":"/docs/Blockchain/Module3/mining","docId":"Blockchain/Module3/mining"},{"type":"link","label":"Bitcoin mining hardware","href":"/docs/Blockchain/Module3/mining-hardware","docId":"Blockchain/Module3/mining-hardware"},{"type":"link","label":"Bitcoin Hashpower","href":"/docs/Blockchain/Module3/hashpower","docId":"Blockchain/Module3/hashpower"},{"type":"link","label":"Proof of Work","href":"/docs/Blockchain/Module3/PoW","docId":"Blockchain/Module3/PoW"},{"type":"link","label":"Hardforks of Blockchain","href":"/docs/Blockchain/Module3/forking","docId":"Blockchain/Module3/forking"},{"type":"link","label":"Block finality","href":"/docs/Blockchain/Module3/finality","docId":"Blockchain/Module3/finality"},{"type":"link","label":"Proof of Staking","href":"/docs/Blockchain/Module3/PoS","docId":"Blockchain/Module3/PoS"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Nodes and Networking","items":[{"type":"link","label":"Introduction to Distributed Systems","href":"/docs/Blockchain/Module4/distributed","docId":"Blockchain/Module4/distributed"},{"type":"link","label":"Network Consensus","href":"/docs/Blockchain/Module4/consensus","docId":"Blockchain/Module4/consensus"},{"type":"link","label":"Network Stack","href":"/docs/Blockchain/Module4/networkstack","docId":"Blockchain/Module4/networkstack"},{"type":"link","label":"How do Blockchain nodes communicate?","href":"/docs/Blockchain/Module4/gossip","docId":"Blockchain/Module4/gossip"},{"type":"link","label":"Types of Network Nodes","href":"/docs/Blockchain/Module4/nodes","docId":"Blockchain/Module4/nodes"},{"type":"link","label":"Networking Challenges and Opportunities","href":"/docs/Blockchain/Module4/network-challenges","docId":"Blockchain/Module4/network-challenges"},{"type":"link","label":"Light clients and Trustless Networks","href":"/docs/Blockchain/Module4/lightclients","docId":"Blockchain/Module4/lightclients"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Layers and Applications","items":[{"type":"link","label":"What are Blockchain Layers?","href":"/docs/Blockchain/Module5/layers","docId":"Blockchain/Module5/layers"},{"type":"link","label":"Layer 0 Blockchains","href":"/docs/Blockchain/Module5/layer0","docId":"Blockchain/Module5/layer0"},{"type":"link","label":"Layer 1 Blockchains","href":"/docs/Blockchain/Module5/layer1","docId":"Blockchain/Module5/layer1"},{"type":"link","label":"Layer 2 Blockchains","href":"/docs/Blockchain/Module5/layer2","docId":"Blockchain/Module5/layer2"},{"type":"link","label":"Enterprise Blockchains","href":"/docs/Blockchain/Module5/enterprise","docId":"Blockchain/Module5/enterprise"},{"type":"link","label":"ZK Proofs and Applications","href":"/docs/Blockchain/Module5/zk-proofs","docId":"Blockchain/Module5/zk-proofs"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Web3 and the future of Blockchain","items":[{"type":"link","label":"Introduction to Web3?","href":"/docs/Blockchain/Module6/web3","docId":"Blockchain/Module6/web3"},{"type":"link","label":"Cryptocurrencies and DeFi","href":"/docs/Blockchain/Module6/crypto-defi","docId":"Blockchain/Module6/crypto-defi"},{"type":"link","label":"NFTs and Metaverse","href":"/docs/Blockchain/Module6/nft-meta","docId":"Blockchain/Module6/nft-meta"},{"type":"link","label":"DAOs and Governance","href":"/docs/Blockchain/Module6/dao","docId":"Blockchain/Module6/dao"},{"type":"link","label":"Decentralized Computing","href":"/docs/Blockchain/Module6/computing","docId":"Blockchain/Module6/computing"},{"type":"link","label":"Future of Web3","href":"/docs/Blockchain/Module6/future-web3","docId":"Blockchain/Module6/future-web3"}],"collapsed":true,"collapsible":true},{"type":"link","label":"Learn more","href":"https://mooc.web3.foundation/"}],"polkadot":[{"type":"link","label":"Introduction to Polkadot","href":"/docs/introdot","docId":"introdot"},{"type":"category","label":"What is Polkadot?","collapsed":false,"items":[{"type":"link","label":"What is Polkadot?","href":"/docs/Polkadot/Module1/polkadot","docId":"Polkadot/Module1/polkadot"},{"type":"link","label":"Architecture of Polkadot","href":"/docs/Polkadot/Module1/architecture","docId":"Polkadot/Module1/architecture"},{"type":"link","label":"Vision of Polkadot","href":"/docs/Polkadot/Module1/polkadotvision","docId":"Polkadot/Module1/polkadotvision"},{"type":"link","label":"Features of Polkadot","href":"/docs/Polkadot/Module1/features","docId":"Polkadot/Module1/features"},{"type":"link","label":"Becoming a Parachain on Polkadot","href":"/docs/Polkadot/Module1/becomeparachain","docId":"Polkadot/Module1/becomeparachain"}],"collapsible":true},{"type":"category","label":"Interact with Polkadot","collapsed":false,"items":[{"type":"link","label":"Create an Account","href":"/docs/Polkadot/Module2/account","docId":"Polkadot/Module2/account"},{"type":"link","label":"DOT Token Utility","href":"/docs/Polkadot/Module2/dotutility","docId":"Polkadot/Module2/dotutility"},{"type":"link","label":"Exploring Polkadot Network","href":"/docs/Polkadot/Module2/explorenetwork","docId":"Polkadot/Module2/explorenetwork"},{"type":"link","label":"On-Chain Governance","href":"/docs/Polkadot/Module2/governance","docId":"Polkadot/Module2/governance"},{"type":"link","label":"Treasury","href":"/docs/Polkadot/Module2/treasury","docId":"Polkadot/Module2/treasury"}],"collapsible":true},{"type":"category","label":"Consensus and Security","items":[{"type":"link","label":"Polkadot Network Consensus","href":"/docs/Polkadot/Module3/consensus","docId":"Polkadot/Module3/consensus"},{"type":"link","label":"Nominated Proof of Staking","href":"/docs/Polkadot/Module3/npos","docId":"Polkadot/Module3/npos"},{"type":"link","label":"Validators and Nominators","href":"/docs/Polkadot/Module3/maintainers","docId":"Polkadot/Module3/maintainers"},{"type":"link","label":"Securing the Network","href":"/docs/Polkadot/Module3/sharedsecurity","docId":"Polkadot/Module3/sharedsecurity"},{"type":"link","label":"Security and Consensus Improvements","href":"/docs/Polkadot/Module3/securityimprovements","docId":"Polkadot/Module3/securityimprovements"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Cryptography and Networking","items":[{"type":"link","label":"Cryptography","href":"/docs/Polkadot/Module4/cryptography","docId":"Polkadot/Module4/cryptography"},{"type":"link","label":"Networking","href":"/docs/Polkadot/Module4/networking","docId":"Polkadot/Module4/networking"},{"type":"link","label":"Nodes on Polkadot Network","href":"/docs/Polkadot/Module4/nodes","docId":"Polkadot/Module4/nodes"},{"type":"link","label":"Path of a Parachain Block","href":"/docs/Polkadot/Module4/parachainblock","docId":"Polkadot/Module4/parachainblock"},{"type":"link","label":"Decentralization of Network","href":"/docs/Polkadot/Module4/decentralization","docId":"Polkadot/Module4/decentralization"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Interoperability and Scalability","items":[{"type":"link","label":"Interoperability","href":"/docs/Polkadot/Module5/interoperability","docId":"Polkadot/Module5/interoperability"},{"type":"link","label":"Polkadot Bridges","href":"/docs/Polkadot/Module5/bridges","docId":"Polkadot/Module5/bridges"},{"type":"link","label":"Parachains","href":"/docs/Polkadot/Module5/parachains","docId":"Polkadot/Module5/parachains"},{"type":"link","label":"Scalability","href":"/docs/Polkadot/Module5/scalability","docId":"Polkadot/Module5/scalability"},{"type":"link","label":"Polkadot Architecture Improvements","href":"/docs/Polkadot/Module5/architectureimprovements","docId":"Polkadot/Module5/architectureimprovements"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Polkadot for Developers","items":[{"type":"link","label":"Rust for Blockchain Development","href":"/docs/Polkadot/Module6/rust","docId":"Polkadot/Module6/rust"},{"type":"link","label":"Introduction to Substrate","href":"/docs/Polkadot/Module6/substrate","docId":"Polkadot/Module6/substrate"},{"type":"link","label":"Polkadot Test Networks","href":"/docs/Polkadot/Module6/testnets","docId":"Polkadot/Module6/testnets"},{"type":"link","label":"Polkadot JS","href":"/docs/Polkadot/Module6/polkadotjs","docId":"Polkadot/Module6/polkadotjs"},{"type":"link","label":"Developer Community","href":"/docs/Polkadot/Module6/developers","docId":"Polkadot/Module6/developers"}],"collapsed":true,"collapsible":true},{"type":"link","label":"Learn more","href":"https://wiki.polkadot.network/"}],"rust":[{"type":"link","label":"Course Appendix","href":"/docs/Rust/rust-appendix","docId":"Rust/rust-appendix"},{"type":"link","label":"Course Syllabus","href":"/docs/introrust","docId":"introrust"},{"type":"category","label":"Why Rust?","items":[{"type":"link","label":"What is Rust?","href":"/docs/Rust/section1/what-is-rust","docId":"Rust/section1/what-is-rust"},{"type":"link","label":"Why Learn Rust?","href":"/docs/Rust/section1/why-rust","docId":"Rust/section1/why-rust"},{"type":"link","label":"WASM TLDR","href":"/docs/Rust/section1/wasm-tldr","docId":"Rust/section1/wasm-tldr"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section1/"},{"type":"link","label":"Installing & Setting up a Rust Developer Environment","href":"/docs/Rust/setup/installation","docId":"Rust/setup/installation"},{"type":"category","label":"Rust 101 - Intro to Basic Rust","items":[{"type":"link","label":"Variables & Mutability","href":"/docs/Rust/section2/variables-mutability","docId":"Rust/section2/variables-mutability"},{"type":"link","label":"The Heap vs. The Stack","href":"/docs/Rust/section2/heap-vs-stack","docId":"Rust/section2/heap-vs-stack"},{"type":"link","label":"Data Types","href":"/docs/Rust/section2/data-types","docId":"Rust/section2/data-types"},{"type":"link","label":"Functions & Comments","href":"/docs/Rust/section2/functions-comments","docId":"Rust/section2/functions-comments"},{"type":"link","label":"Loops & Basic Logic Flows","href":"/docs/Rust/section2/loops","docId":"Rust/section2/loops"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section2/"},{"type":"category","label":"Intro to Intermediate Rust: Ownership, Borrowing, & Slices","items":[{"type":"link","label":"The Ownership Model","href":"/docs/Rust/section3/ownership","docId":"Rust/section3/ownership"},{"type":"link","label":"Borrowing & References","href":"/docs/Rust/section3/borrowing","docId":"Rust/section3/borrowing"},{"type":"link","label":"Slices","href":"/docs/Rust/section3/slices","docId":"Rust/section3/slices"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section3/"},{"type":"category","label":"Intro to Intermediate Rust: Enums & Matching Patterns","items":[{"type":"link","label":"Enums","href":"/docs/Rust/section4/enums","docId":"Rust/section4/enums"},{"type":"link","label":"Panic! in Rust","href":"/docs/Rust/section4/panic","docId":"Rust/section4/panic"},{"type":"link","label":"Error handling with Result & Option","href":"/docs/Rust/section4/error-handling","docId":"Rust/section4/error-handling"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section4/"},{"type":"category","label":"Intro to Intermediate Rust: Data Structs & Collections","items":[{"type":"link","label":"Collections - Vectors, Strings, Hashmaps","href":"/docs/Rust/section5/collections","docId":"Rust/section5/collections"},{"type":"link","label":"Data Structs","href":"/docs/Rust/section5/structs","docId":"Rust/section5/structs"},{"type":"link","label":"Defining Methods for Structs","href":"/docs/Rust/section5/struct-methods","docId":"Rust/section5/struct-methods"},{"type":"link","label":"Vectors, Strings, and &str Slices","href":"/docs/Rust/section5/vectors-vs-strings","docId":"Rust/section5/vectors-vs-strings"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section5/"},{"type":"category","label":"Intro to Advanced Rust: Traits, Generics, & Lifetimes","items":[{"type":"link","label":"Traits","href":"/docs/Rust/section6/traits","docId":"Rust/section6/traits"},{"type":"link","label":"Generic Types","href":"/docs/Rust/section6/generics","docId":"Rust/section6/generics"},{"type":"link","label":"Associated Types vs Generic Types","href":"/docs/Rust/section6/associated-generics","docId":"Rust/section6/associated-generics"},{"type":"link","label":"Lifetimes","href":"/docs/Rust/section6/lifetimes","docId":"Rust/section6/lifetimes"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section6/"},{"type":"category","label":"Intro to Advanced Rust: Iterators & Closures","items":[{"type":"link","label":"Iterators","href":"/docs/Rust/section7/iterators","docId":"Rust/section7/iterators"},{"type":"link","label":"Closures","href":"/docs/Rust/section7/closures","docId":"Rust/section7/closures"},{"type":"link","label":"Macros in Rust","href":"/docs/Rust/section7/macros","docId":"Rust/section7/macros"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section7/"},{"type":"category","label":"Learning Cargo, Rust\u2019s Package Management\\t System, Basic Testing","items":[{"type":"link","label":"Understanding Cargo","href":"/docs/Rust/section8/defining-cargo-config","docId":"Rust/section8/defining-cargo-config"},{"type":"link","label":"Installing a crate","href":"/docs/Rust/section8/installing-crate","docId":"Rust/section8/installing-crate"},{"type":"link","label":"Specifying features in crates","href":"/docs/Rust/section8/defining-crate-features","docId":"Rust/section8/defining-crate-features"},{"type":"link","label":"Unit Testing","href":"/docs/Rust/section8/unit-tests","docId":"Rust/section8/unit-tests"}],"collapsed":true,"collapsible":true,"href":"/docs/Rust/section8/"},{"type":"link","label":"Learn more","href":"https://www.rust-lang.org/"}],"substrate":[{"type":"link","label":"Course Syllabus","href":"/docs/introsubstrate","docId":"introsubstrate"},{"type":"category","label":"Introduction to Substrate","items":[{"type":"link","label":"What is Substrate and FRAME?","href":"/docs/Substrate/section1/what-is-substrate","docId":"Substrate/section1/what-is-substrate"},{"type":"link","label":"History Behind Substrate","href":"/docs/Substrate/section1/substrate-history","docId":"Substrate/section1/substrate-history"},{"type":"link","label":"Substrate\u2019s Design Choices","href":"/docs/Substrate/section1/substrate-design","docId":"Substrate/section1/substrate-design"},{"type":"link","label":"Course Capstone Project","href":"/docs/Substrate/section1/capstone-project","docId":"Substrate/section1/capstone-project"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section1/"},{"type":"category","label":"Substrate Development 101","items":[{"type":"link","label":"Runtime & Outer Node","href":"/docs/Substrate/section2/substrate-runtime","docId":"Substrate/section2/substrate-runtime"},{"type":"link","label":"Storage","href":"/docs/Substrate/section2/substrate-storage","docId":"Substrate/section2/substrate-storage"},{"type":"link","label":"Pallets","href":"/docs/Substrate/section2/substrate-pallets","docId":"Substrate/section2/substrate-pallets"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section2/"},{"type":"category","label":"Installing Dependencies & Node Template","items":[{"type":"link","label":"Install, configure, and run the Node Template","href":"/docs/Substrate/section3/install-deps","docId":"Substrate/section3/install-deps"},{"type":"link","label":"Node Template Tour & Overview","href":"/docs/Substrate/section3/node-template-tour","docId":"Substrate/section3/node-template-tour"},{"type":"link","label":"Exploring the pallet template","href":"/docs/Substrate/section3/explore-pallet-template","docId":"Substrate/section3/explore-pallet-template"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section3/"},{"type":"category","label":"Building a Custom FRAME Pallet: Intro","items":[{"type":"link","label":"Adjusting Pallet Config & Runtime Overview","href":"/docs/Substrate/section4/pallet-config","docId":"Substrate/section4/pallet-config"},{"type":"link","label":"lib.rs Structure Deepdive","href":"/docs/Substrate/section4/project-structure","docId":"Substrate/section4/project-structure"},{"type":"link","label":"Creating storage maps","href":"/docs/Substrate/section4/create-storage-map","docId":"Substrate/section4/create-storage-map"},{"type":"link","label":"Events & Errors","href":"/docs/Substrate/section4/events-errors","docId":"Substrate/section4/events-errors"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section4/"},{"type":"category","label":"Building a Custom FRAME Pallet: Making our pallet actionable","items":[{"type":"link","label":"Creating dispatchable functions","href":"/docs/Substrate/section5/dispatchable","docId":"Substrate/section5/dispatchable"},{"type":"link","label":"Writing unit tests for pallets","href":"/docs/Substrate/section5/unit-tests","docId":"Substrate/section5/unit-tests"},{"type":"link","label":"Using other pallets","href":"/docs/Substrate/section5/coupling-pallets","docId":"Substrate/section5/coupling-pallets"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section5/"},{"type":"category","label":"Building a Custom FRAME Pallet: Deploying & Testing","items":[{"type":"link","label":"Running the node template","href":"/docs/Substrate/section6/run-node","docId":"Substrate/section6/run-node"},{"type":"link","label":"Use the frontend-template to test your pallet","href":"/docs/Substrate/section6/test-frontend","docId":"Substrate/section6/test-frontend"},{"type":"link","label":"Using Polkadot.js to explore your storage","href":"/docs/Substrate/section6/use-polkadotjs","docId":"Substrate/section6/use-polkadotjs"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section6/"},{"type":"category","label":"Building a Custom FRAME Pallet: Pallet & FRAME best practices","items":[{"type":"link","label":"Thinking in terms of blockchain development","href":"/docs/Substrate/section7/blockchain-dev","docId":"Substrate/section7/blockchain-dev"},{"type":"link","label":"Runtime Panics & FRAME Best Practices","href":"/docs/Substrate/section7/runtime-panics","docId":"Substrate/section7/runtime-panics"},{"type":"link","label":"How to approach testing in FRAME","href":"/docs/Substrate/section7/how-to-test-frame","docId":"Substrate/section7/how-to-test-frame"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section7/"},{"type":"category","label":"FRAME Deepdive","items":[{"type":"link","label":"Origins (Privileged Calls) in FRAME","href":"/docs/Substrate/section8/origins-calls","docId":"Substrate/section8/origins-calls"},{"type":"link","label":"Pallet Coupling","href":"/docs/Substrate/section8/pallet-coupling","docId":"Substrate/section8/pallet-coupling"},{"type":"link","label":"Chain Specification","href":"/docs/Substrate/section8/chain-genesis-spec","docId":"Substrate/section8/chain-genesis-spec"},{"type":"link","label":"Introduction to Benchmarking","href":"/docs/Substrate/section8/benchmarking","docId":"Substrate/section8/benchmarking"}],"collapsed":true,"collapsible":true,"href":"/docs/Substrate/section8/"},{"type":"link","label":"Learn more","href":"https://docs.substrate.io/"}],"web3":[{"type":"link","label":"Web3","href":"/docs/introweb3","docId":"introweb3"},{"type":"link","label":"Learn more","href":"https://web3.foundation/"}],"parachain":[{"type":"link","label":"Course Syllabus","href":"/docs/introparachain","docId":"introparachain"},{"type":"category","label":"Parachain Architecture Overview","items":[{"type":"link","label":"Polkadot Network Components","href":"/docs/Parachain/beginner/section1/network-components","docId":"Parachain/beginner/section1/network-components"},{"type":"link","label":"Relay Chain Architecture Overview","href":"/docs/Parachain/beginner/section1/relay-chain","docId":"Parachain/beginner/section1/relay-chain"},{"type":"link","label":"Parachain Architecture Overview","href":"/docs/Parachain/beginner/section1/parachain","docId":"Parachain/beginner/section1/parachain"}],"collapsed":true,"collapsible":true,"href":"/docs/Parachain/beginner/section1/"},{"type":"category","label":"Dependency Installation","items":[{"type":"link","label":"Install Local Binaries","href":"/docs/Parachain/beginner/section2/install-binary","docId":"Parachain/beginner/section2/install-binary"},{"type":"link","label":"Install the Cumulus Parachain Template","href":"/docs/Parachain/beginner/section2/install-template","docId":"Parachain/beginner/section2/install-template"},{"type":"link","label":"Running the Relay and Parachain","href":"/docs/Parachain/beginner/section2/running-chains","docId":"Parachain/beginner/section2/running-chains"}],"collapsed":true,"collapsible":true,"href":"/docs/Parachain/beginner/section2/"},{"type":"category","label":"Creating & Registering Parachain","items":[{"type":"link","label":"Creating a fast-tracked auction","href":"/docs/Parachain/beginner/section3/creating-auction","docId":"Parachain/beginner/section3/creating-auction"},{"type":"link","label":"Reserve your parathread","href":"/docs/Parachain/beginner/section3/creating-parathread","docId":"Parachain/beginner/section3/creating-parathread"},{"type":"link","label":"Developing your parachain with FRAME","href":"/docs/Parachain/beginner/section3/developing-parachain","docId":"Parachain/beginner/section3/developing-parachain"}],"collapsed":true,"collapsible":true,"href":"/docs/Parachain/beginner/section3/"},{"type":"category","label":"Moving Forward","items":[{"type":"link","label":"Ecosystem Initiatives","href":"/docs/Parachain/beginner/section4/initatives","docId":"Parachain/beginner/section4/initatives"},{"type":"link","label":"Road to Production","href":"/docs/Parachain/beginner/section4/road-to-production","docId":"Parachain/beginner/section4/road-to-production"}],"collapsed":true,"collapsible":true,"href":"/docs/Parachain/beginner/section4/"}],"atoz":[{"type":"link","label":"A to Z ELI5 Series","href":"/docs/introatoz","docId":"introatoz"},{"type":"link","label":"A for Account","href":"/docs/AtoZ/account","docId":"AtoZ/account"},{"type":"link","label":"B for Bridge","href":"/docs/AtoZ/bridge","docId":"AtoZ/bridge"},{"type":"link","label":"C for Collator","href":"/docs/AtoZ/collator","docId":"AtoZ/collator"},{"type":"link","label":"D for Democracy","href":"/docs/AtoZ/democracy","docId":"AtoZ/democracy"},{"type":"link","label":"E for Existential Deposit","href":"/docs/AtoZ/existential-deposit","docId":"AtoZ/existential-deposit"},{"type":"link","label":"F for Forkless","href":"/docs/AtoZ/forkless","docId":"AtoZ/forkless"},{"type":"link","label":"G for Grandpa","href":"/docs/AtoZ/grandpa","docId":"AtoZ/grandpa"},{"type":"link","label":"H for Hash","href":"/docs/AtoZ/hash","docId":"AtoZ/hash"},{"type":"link","label":"I for Interoperability","href":"/docs/AtoZ/interoperability","docId":"AtoZ/interoperability"},{"type":"link","label":"J for Polkadot JS","href":"/docs/AtoZ/polkadot-js","docId":"AtoZ/polkadot-js"},{"type":"link","label":"K for Kusama","href":"/docs/AtoZ/kusama","docId":"AtoZ/kusama"},{"type":"link","label":"L for Polkadot Launch","href":"/docs/AtoZ/launch","docId":"AtoZ/launch"},{"type":"link","label":"M for Multisig","href":"/docs/AtoZ/multisig","docId":"AtoZ/multisig"},{"type":"link","label":"N for Nominated Proof of Stake","href":"/docs/AtoZ/npos","docId":"AtoZ/npos"},{"type":"link","label":"O for On-chain Governance","href":"/docs/AtoZ/on-chain-governance","docId":"AtoZ/on-chain-governance"},{"type":"link","label":"P for Phragm\xe9n","href":"/docs/AtoZ/phragm\xe9n","docId":"AtoZ/phragm\xe9n"},{"type":"link","label":"Q for FAQ","href":"/docs/AtoZ/q-faq","docId":"AtoZ/q-faq"}]},"docs":{"AtoZ/account":{"id":"AtoZ/account","title":"A for Account","description":"A for Account","sidebar":"atoz"},"AtoZ/bridge":{"id":"AtoZ/bridge","title":"B for Bridge","description":"B for Bridges","sidebar":"atoz"},"AtoZ/collator":{"id":"AtoZ/collator","title":"C for Collator","description":"C for Collators","sidebar":"atoz"},"AtoZ/democracy":{"id":"AtoZ/democracy","title":"D for Democracy","description":"D for Democracy","sidebar":"atoz"},"AtoZ/existential-deposit":{"id":"AtoZ/existential-deposit","title":"E for Existential Deposit","description":"E for Existential Deposit","sidebar":"atoz"},"AtoZ/forkless":{"id":"AtoZ/forkless","title":"F for Forkless","description":"F for Forkless","sidebar":"atoz"},"AtoZ/grandpa":{"id":"AtoZ/grandpa","title":"G for GRANDPA","description":"G for GRANDPA","sidebar":"atoz"},"AtoZ/hash":{"id":"AtoZ/hash","title":"H for Hash","description":"H for Hash","sidebar":"atoz"},"AtoZ/interoperability":{"id":"AtoZ/interoperability","title":"I for Interoperability","description":"I for Interoperability","sidebar":"atoz"},"AtoZ/kusama":{"id":"AtoZ/kusama","title":"K for Kusama","description":"K for Kusama","sidebar":"atoz"},"AtoZ/launch":{"id":"AtoZ/launch","title":"L for Polkadot Launch","description":"L for Polkadot Launch","sidebar":"atoz"},"AtoZ/multisig":{"id":"AtoZ/multisig","title":"M for Multisig","description":"M for Multisig","sidebar":"atoz"},"AtoZ/npos":{"id":"AtoZ/npos","title":"N for Nominated Proof of Stake (NPoS)","description":"N for Nominated Proof of Stake (NPoS)","sidebar":"atoz"},"AtoZ/on-chain-governance":{"id":"AtoZ/on-chain-governance","title":"O for On-chain Governance","description":"O for On-chain Governance","sidebar":"atoz"},"AtoZ/phragm\xe9n":{"id":"AtoZ/phragm\xe9n","title":"P for Phragm\xe9n","description":"P for Phragm\xe9n","sidebar":"atoz"},"AtoZ/polkadot-js":{"id":"AtoZ/polkadot-js","title":"J for Polkadot JS","description":"J for Polkadot JS","sidebar":"atoz"},"AtoZ/q-faq":{"id":"AtoZ/q-faq","title":"Q for FAQ","description":"J for Polkadot JS","sidebar":"atoz"},"Blockchain/Module1/bitcoin":{"id":"Blockchain/Module1/bitcoin","title":"Introduction to Bitcoin","description":"","sidebar":"blockchain"},"Blockchain/Module1/blockchain":{"id":"Blockchain/Module1/blockchain","title":"What is a Blockchain?","description":"- A data structure","sidebar":"blockchain"},"Blockchain/Module1/digital-money":{"id":"Blockchain/Module1/digital-money","title":"Dawn of Digital Money","description":"","sidebar":"blockchain"},"Blockchain/Module1/ethereum":{"id":"Blockchain/Module1/ethereum","title":"Introduction to Ethereum","description":"","sidebar":"blockchain"},"Blockchain/Module1/money-properties":{"id":"Blockchain/Module1/money-properties","title":"Properties of Money","description":"","sidebar":"blockchain"},"Blockchain/Module1/money-trust":{"id":"Blockchain/Module1/money-trust","title":"History of Money and Trust","description":"","sidebar":"blockchain"},"Blockchain/Module1/web3":{"id":"Blockchain/Module1/web3","title":"Introduction to Web3","description":"","sidebar":"blockchain"},"Blockchain/Module2/cryptography":{"id":"Blockchain/Module2/cryptography","title":"Cryptography and Blockchain","description":"","sidebar":"blockchain"},"Blockchain/Module2/datastructures":{"id":"Blockchain/Module2/datastructures","title":"Blockchain Datastructures","description":"","sidebar":"blockchain"},"Blockchain/Module2/decentralization":{"id":"Blockchain/Module2/decentralization","title":"Decentralization Trilemma","description":"","sidebar":"blockchain"},"Blockchain/Module2/hash":{"id":"Blockchain/Module2/hash","title":"Cryptographic Hashing","description":"","sidebar":"blockchain"},"Blockchain/Module2/invincible":{"id":"Blockchain/Module2/invincible","title":"How Invincible is Bitcoin Network?","description":"","sidebar":"blockchain"},"Blockchain/Module2/keypair":{"id":"Blockchain/Module2/keypair","title":"Digital Signatures and Crypto-wallets","description":"","sidebar":"blockchain"},"Blockchain/Module2/trust":{"id":"Blockchain/Module2/trust","title":"Trust issues","description":"","sidebar":"blockchain"},"Blockchain/Module3/finality":{"id":"Blockchain/Module3/finality","title":"Block finality","description":"","sidebar":"blockchain"},"Blockchain/Module3/forking":{"id":"Blockchain/Module3/forking","title":"Hardforks of Blockchain","description":"","sidebar":"blockchain"},"Blockchain/Module3/hashpower":{"id":"Blockchain/Module3/hashpower","title":"Bitcoin Hashpower","description":"","sidebar":"blockchain"},"Blockchain/Module3/mining":{"id":"Blockchain/Module3/mining","title":"How Bitcoin blocks are mined?","description":"","sidebar":"blockchain"},"Blockchain/Module3/mining-hardware":{"id":"Blockchain/Module3/mining-hardware","title":"Bitcoin mining hardware","description":"","sidebar":"blockchain"},"Blockchain/Module3/PoS":{"id":"Blockchain/Module3/PoS","title":"Proof of Staking","description":"","sidebar":"blockchain"},"Blockchain/Module3/PoW":{"id":"Blockchain/Module3/PoW","title":"Proof of Work","description":"","sidebar":"blockchain"},"Blockchain/Module4/consensus":{"id":"Blockchain/Module4/consensus","title":"Network Consensus","description":"","sidebar":"blockchain"},"Blockchain/Module4/distributed":{"id":"Blockchain/Module4/distributed","title":"Introduction to Distributed Systems","description":"","sidebar":"blockchain"},"Blockchain/Module4/gossip":{"id":"Blockchain/Module4/gossip","title":"How do Blockchain nodes communicate?","description":"","sidebar":"blockchain"},"Blockchain/Module4/lightclients":{"id":"Blockchain/Module4/lightclients","title":"Light clients and Trustless Networks","description":"","sidebar":"blockchain"},"Blockchain/Module4/network-challenges":{"id":"Blockchain/Module4/network-challenges","title":"Networking Challenges and Opportunities","description":"","sidebar":"blockchain"},"Blockchain/Module4/networkstack":{"id":"Blockchain/Module4/networkstack","title":"Network Stack","description":"","sidebar":"blockchain"},"Blockchain/Module4/nodes":{"id":"Blockchain/Module4/nodes","title":"Types of Network Nodes","description":"","sidebar":"blockchain"},"Blockchain/Module5/enterprise":{"id":"Blockchain/Module5/enterprise","title":"Enterprise Blockchains","description":"","sidebar":"blockchain"},"Blockchain/Module5/layer0":{"id":"Blockchain/Module5/layer0","title":"Layer 0 Blockchains","description":"","sidebar":"blockchain"},"Blockchain/Module5/layer1":{"id":"Blockchain/Module5/layer1","title":"Layer 1 Blockchains","description":"","sidebar":"blockchain"},"Blockchain/Module5/layer2":{"id":"Blockchain/Module5/layer2","title":"Layer 2 Blockchains","description":"","sidebar":"blockchain"},"Blockchain/Module5/layers":{"id":"Blockchain/Module5/layers","title":"What are Blockchain Layers?","description":"","sidebar":"blockchain"},"Blockchain/Module5/zk-proofs":{"id":"Blockchain/Module5/zk-proofs","title":"ZK Proofs and Applications","description":"","sidebar":"blockchain"},"Blockchain/Module6/computing":{"id":"Blockchain/Module6/computing","title":"Decentralized Computing","description":"","sidebar":"blockchain"},"Blockchain/Module6/crypto-defi":{"id":"Blockchain/Module6/crypto-defi","title":"Cryptocurrencies and DeFi","description":"","sidebar":"blockchain"},"Blockchain/Module6/dao":{"id":"Blockchain/Module6/dao","title":"DAOs and Governance","description":"","sidebar":"blockchain"},"Blockchain/Module6/future-web3":{"id":"Blockchain/Module6/future-web3","title":"Future of Web3","description":"","sidebar":"blockchain"},"Blockchain/Module6/nft-meta":{"id":"Blockchain/Module6/nft-meta","title":"NFTs and Metaverse","description":"","sidebar":"blockchain"},"Blockchain/Module6/web3":{"id":"Blockchain/Module6/web3","title":"Introduction to Web3?","description":"","sidebar":"blockchain"},"intro":{"id":"intro","title":"Web3 Education Initiative","description":"Four courses - Blockchain Fundamentals, Introduction to Polkadot, Introduction to Rust Programming and Introduction to Substrate"},"introatoz":{"id":"introatoz","title":"W3F A - Z ELI5 series","description":"ELI5 - Explain like I am five!","sidebar":"atoz"},"introblock":{"id":"introblock","title":"Blockchain Basics","description":"Developed by the Technical Education team at the Web3 Foundation, this course provides a beginner\'s introduction to Blockchain technology.","sidebar":"blockchain"},"introdot":{"id":"introdot","title":"Introduction to Polkadot","description":"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 \u201cWhat is Polkadot?\u201d. 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.","sidebar":"polkadot"},"introparachain":{"id":"introparachain","title":"Parachain Development Guide","description":"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.","sidebar":"parachain"},"introrust":{"id":"introrust","title":"Introduction to Rust for Blockchain Development","description":"Developed by the Technical Education team at the Web3 Foundation, this course introduces programming in Rust for Blockchain applications.","sidebar":"rust"},"introsubstrate":{"id":"introsubstrate","title":"Introduction to Substrate & FRAME","description":"Developed by the Technical Education team at the Web3 Foundation, this course provides a comprehensive overview of the Substrate blockchain framework and building blockchains.","sidebar":"substrate"},"introweb3":{"id":"introweb3","title":"Introduction to Web3","description":"Developed by the Technical Education team at the Web3 Foundation, this course provides a beginner\'s introduction to Web3. Less trust, more truth.","sidebar":"web3"},"Parachain/beginner/section1/index":{"id":"Parachain/beginner/section1/index","title":"Parachain Architecture Overview","description":"","sidebar":"parachain"},"Parachain/beginner/section1/network-components":{"id":"Parachain/beginner/section1/network-components","title":"Polkadot Network Components","description":"Get a brief overview of how the Polkadot network operates and crucial components.","sidebar":"parachain"},"Parachain/beginner/section1/parachain":{"id":"Parachain/beginner/section1/parachain","title":"Parachain Architecture Overview","description":"Learn the structure of a parachain, what tools are used to develop it, and how it relates to the relay chain.","sidebar":"parachain"},"Parachain/beginner/section1/relay-chain":{"id":"Parachain/beginner/section1/relay-chain","title":"Relay Chain Architecture Overview","description":"Learn the role of the relay chain, and how it registers and validates parachains.","sidebar":"parachain"},"Parachain/beginner/section2/index":{"id":"Parachain/beginner/section2/index","title":"Dependency Installation","description":"","sidebar":"parachain"},"Parachain/beginner/section2/install-binary":{"id":"Parachain/beginner/section2/install-binary","title":"Install Local Binaries","description":"Install the necessary Polkadot binaries onto your local machine.","sidebar":"parachain"},"Parachain/beginner/section2/install-template":{"id":"Parachain/beginner/section2/install-template","title":"Install the Cumulus Parachain Template","description":"Install the Cumulus Parachain Template","sidebar":"parachain"},"Parachain/beginner/section2/running-chains":{"id":"Parachain/beginner/section2/running-chains","title":"Running the Relay and Parachain","description":"Ensure both the relay chain and parachain run locally.","sidebar":"parachain"},"Parachain/beginner/section3/creating-auction":{"id":"Parachain/beginner/section3/creating-auction","title":"Creating a fast-tracked auction","description":"Creating and registering your parathread via auction.","sidebar":"parachain"},"Parachain/beginner/section3/creating-parathread":{"id":"Parachain/beginner/section3/creating-parathread","title":"Reserve your parathread","description":"Reserve your parathread and para ID.","sidebar":"parachain"},"Parachain/beginner/section3/developing-parachain":{"id":"Parachain/beginner/section3/developing-parachain","title":"Developing your parachain with FRAME","description":"Configure and customize your parachain before registering it on the relay chain.","sidebar":"parachain"},"Parachain/beginner/section3/index":{"id":"Parachain/beginner/section3/index","title":"Creating & Registering Parachain","description":"","sidebar":"parachain"},"Parachain/beginner/section4/index":{"id":"Parachain/beginner/section4/index","title":"Moving Forward","description":"","sidebar":"parachain"},"Parachain/beginner/section4/initatives":{"id":"Parachain/beginner/section4/initatives","title":"Ecosystem Initiatives","description":"Explore different avenues for expanding your project within the Polkadot ecosystem.","sidebar":"parachain"},"Parachain/beginner/section4/road-to-production":{"id":"Parachain/beginner/section4/road-to-production","title":"Road to Production","description":"Learn the next technical steps to put your parachain to the road of production.","sidebar":"parachain"},"Polkadot/Module1/architecture":{"id":"Polkadot/Module1/architecture","title":"Architecture of Polkadot","description":"Polkadot utilises a central chain called the relay chain which communicates with mul-","sidebar":"polkadot"},"Polkadot/Module1/becomeparachain":{"id":"Polkadot/Module1/becomeparachain","title":"Becoming a Parachain on Polkadot","description":"The Polkadot system consists of a single open collaborative decentralised network called the","sidebar":"polkadot"},"Polkadot/Module1/features":{"id":"Polkadot/Module1/features","title":"Features of Polkadot","description":"Bitcoin and Ethereumare proof-of-work (PoW) blockchains where security relies","sidebar":"polkadot"},"Polkadot/Module1/polkadot":{"id":"Polkadot/Module1/polkadot","title":"What is Polkadot?","description":"Polkadot is a heterogenous multi-chain protocol. Polkadot addresses some of the existing shortcomings","sidebar":"polkadot"},"Polkadot/Module1/polkadotvision":{"id":"Polkadot/Module1/polkadotvision","title":"Vision of Polkadot","description":"The Internet was originally designed for and built upon decentralised protocols such as TCP/IP,","sidebar":"polkadot"},"Polkadot/Module2/account":{"id":"Polkadot/Module2/account","title":"Create an Account","description":"","sidebar":"polkadot"},"Polkadot/Module2/dotutility":{"id":"Polkadot/Module2/dotutility","title":"DOT Token Utility","description":"On the side of economics, we aim to have a controlled near-constant yearly inflation","sidebar":"polkadot"},"Polkadot/Module2/explorenetwork":{"id":"Polkadot/Module2/explorenetwork","title":"Exploring Polkadot Network","description":"Polkadot as a State Machine","sidebar":"polkadot"},"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","sidebar":"polkadot"},"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","sidebar":"polkadot"},"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","sidebar":"polkadot"},"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","sidebar":"polkadot"},"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","sidebar":"polkadot"},"Polkadot/Module3/securityimprovements":{"id":"Polkadot/Module3/securityimprovements","title":"Security and Consensus Improvements","description":"Sassafras","sidebar":"polkadot"},"Polkadot/Module3/sharedsecurity":{"id":"Polkadot/Module3/sharedsecurity","title":"Securing the Network","description":"Polkadot assumes that parachains are running as external untrusted clients of the relay chain and that","sidebar":"polkadot"},"Polkadot/Module4/cryptography":{"id":"Polkadot/Module4/cryptography","title":"Cryptography","description":"We assume that malicious parties generate their keys with an arbitrary algorithm while","sidebar":"polkadot"},"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 -","sidebar":"polkadot"},"Polkadot/Module4/networking":{"id":"Polkadot/Module4/networking","title":"Networking","description":"All validators have their own local clock and their clocks do","sidebar":"polkadot"},"Polkadot/Module4/nodes":{"id":"Polkadot/Module4/nodes","title":"Nodes on Polkadot Network","description":"The Polkadot relay chain network consists of nodes and roles. Nodes are the network-level enti-","sidebar":"polkadot"},"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","sidebar":"polkadot"},"Polkadot/Module5/architectureimprovements":{"id":"Polkadot/Module5/architectureimprovements","title":"Polkadot Architecture Improvements","description":"XCMP","sidebar":"polkadot"},"Polkadot/Module5/bridges":{"id":"Polkadot/Module5/bridges","title":"Polkadot Bridges","description":"Polkadot-Kusama Bridge","sidebar":"polkadot"},"Polkadot/Module5/interoperability":{"id":"Polkadot/Module5/interoperability","title":"Interoperability","description":"XCMP","sidebar":"polkadot"},"Polkadot/Module5/parachains":{"id":"Polkadot/Module5/parachains","title":"Parachains","description":"","sidebar":"polkadot"},"Polkadot/Module5/scalability":{"id":"Polkadot/Module5/scalability","title":"Scalability","description":"Validity and Availability","sidebar":"polkadot"},"Polkadot/Module6/developers":{"id":"Polkadot/Module6/developers","title":"Developer Community","description":"Community Contributions","sidebar":"polkadot"},"Polkadot/Module6/polkadotjs":{"id":"Polkadot/Module6/polkadotjs","title":"Polkadot JS","description":"","sidebar":"polkadot"},"Polkadot/Module6/rust":{"id":"Polkadot/Module6/rust","title":"Rust for Blockchain Development","description":"","sidebar":"polkadot"},"Polkadot/Module6/substrate":{"id":"Polkadot/Module6/substrate","title":"Introduction to Substrate","description":"Substrate - A Blockchain Building Framework","sidebar":"polkadot"},"Polkadot/Module6/testnets":{"id":"Polkadot/Module6/testnets","title":"Polkadot Test Networks","description":"","sidebar":"polkadot"},"polkadotFAQ":{"id":"polkadotFAQ","title":"Polkadot FAQ","description":"Where can I find a list of academic and in-depth articles and information about Polkadot?"},"Rust/rust-appendix":{"id":"Rust/rust-appendix","title":"Course Appendix","description":"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.","sidebar":"rust"},"Rust/section1/index":{"id":"Rust/section1/index","title":"Why Learn Rust?","description":"Welcome to the Introduction to Rust course by Web3 Foundation. This introductory course focuses on bringing the most impactful components of the Rust Programming Language to light and it will focus on the key concepts to start your Web3 learning journey.","sidebar":"rust"},"Rust/section1/wasm-tldr":{"id":"Rust/section1/wasm-tldr","title":"WASM TLDR - What is WASM, and why is it important?","description":"Why WASM is used, and it\'s support in Rust.","sidebar":"rust"},"Rust/section1/what-is-rust":{"id":"Rust/section1/what-is-rust","title":"What is Rust?","description":"An introduction to what the Rust programming language is, and how it compares to other languages.","sidebar":"rust"},"Rust/section1/why-rust":{"id":"Rust/section1/why-rust","title":"Why Learn Rust?","description":"A brief overview of the pros of learning the Rust programming language.","sidebar":"rust"},"Rust/section2/data-types":{"id":"Rust/section2/data-types","title":"Data Types in Rust","description":"Learn what data types are and which basic ones exist.","sidebar":"rust"},"Rust/section2/functions-comments":{"id":"Rust/section2/functions-comments","title":"Functions & Comments in Rust","description":"Learn how functions and comments work, and how to create them in Rust.","sidebar":"rust"},"Rust/section2/heap-vs-stack":{"id":"Rust/section2/heap-vs-stack","title":"The Heap vs. The Stack","description":"Learn the summarized differences between the stack and the heap and how they affect your Rust programming.","sidebar":"rust"},"Rust/section2/index":{"id":"Rust/section2/index","title":"Rust 101 - Intro to Basic Rust","description":"The second module will dive right into the very basics of Rust. Learn how variables, scope, and mutability work together, along with basic data types and how Rust can create functions. Lastly, learn how to perform iterative operations with loops.","sidebar":"rust"},"Rust/section2/loops":{"id":"Rust/section2/loops","title":"Loops & Basic Logic Flows","description":"Learn how to construct loops, if statements, and when to use the two.","sidebar":"rust"},"Rust/section2/module2":{"id":"Rust/section2/module2","title":"2. Intro to Basic Rust","description":"Variable Scope. Memory management. How variables interact. References. Background on Programming Safety and why it is critical for Blockchain development. Substrate introduction."},"Rust/section2/variables-mutability":{"id":"Rust/section2/variables-mutability","title":"Variables & Mutability","description":"How variables and mutability works in Rust.","sidebar":"rust"},"Rust/section3/borrowing":{"id":"Rust/section3/borrowing","title":"Borrowing & References in Rust","description":"Learn how the borrowing model works in Rust, and how to utilize it properly.","sidebar":"rust"},"Rust/section3/index":{"id":"Rust/section3/index","title":"Intro to Intermediate Rust - Ownership, Borrowing, & Slices","description":"This module will focus on more intermediate concepts relating to Rust\'s ownership and borrowing model.","sidebar":"rust"},"Rust/section3/module3":{"id":"Rust/section3/module3","title":"3. Intro to Intermediate Rust","description":"Structs. Enums. Methods. Packages and Crates."},"Rust/section3/ownership":{"id":"Rust/section3/ownership","title":"Rust\'s Ownership Model","description":"Learn how the ownership model works in Rust, and why it allows for Rust to be safe at compile time.","sidebar":"rust"},"Rust/section3/slices":{"id":"Rust/section3/slices","title":"Slices in Rust","description":"Learn what slices are, how they work, and why they are useful.","sidebar":"rust"},"Rust/section4/enums":{"id":"Rust/section4/enums","title":"Enums in Rust","description":"Learn what enums are and how to use them in Rust.","sidebar":"rust"},"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.","sidebar":"rust"},"Rust/section4/index":{"id":"Rust/section4/index","title":"Intro to Intermediate Rust - Enums & Matching Patterns","description":"In this module, you will become familiar with some of Rust\'s powerful pattern matching and enumeration capabilities.","sidebar":"rust"},"Rust/section4/panic":{"id":"Rust/section4/panic","title":"Panic! in Rust","description":"Discover what panic! in Rust is, and when you should panic.","sidebar":"rust"},"Rust/section5/collections":{"id":"Rust/section5/collections","title":"Collections in Rust - Vectors, Strings, Hashmaps","description":"Learn what collections are, and when to use which in Rust.","sidebar":"rust"},"Rust/section5/index":{"id":"Rust/section5/index","title":"Intro to Intermediate Rust - Data Structs & Collections","description":"This module will delve into some more intermediate concepts: collections and data structs.","sidebar":"rust"},"Rust/section5/struct-methods":{"id":"Rust/section5/struct-methods","title":"Defining Methods for Structs","description":"Learn how to define methods for structs in Rust.","sidebar":"rust"},"Rust/section5/structs":{"id":"Rust/section5/structs","title":"Data Structs in rust","description":"Learn what data structs are in Rust.","sidebar":"rust"},"Rust/section5/vectors-vs-strings":{"id":"Rust/section5/vectors-vs-strings","title":"Vectors, Strings, and &str Slices","description":"Learn the difference between vectors and strings in Rust.","sidebar":"rust"},"Rust/section6/associated-generics":{"id":"Rust/section6/associated-generics","title":"Associated Types vs Generic Types","description":"Learn the difference between associated types vs generic types.","sidebar":"rust"},"Rust/section6/generics":{"id":"Rust/section6/generics","title":"Generic Types in Rust","description":"Learn what generic types are and how to use them in Rust.","sidebar":"rust"},"Rust/section6/index":{"id":"Rust/section6/index","title":"Intro to Advanced Rust - Traits, Generics, & Lifetimes","description":"The following module deals with the concepts of traits, generics, and an overall deeper look at the robust type system that Rust provides. Lifetimes, yet another safety mechanism to ensure that references are living correctly within the program.","sidebar":"rust"},"Rust/section6/lifetimes":{"id":"Rust/section6/lifetimes","title":"Lifetimes in Rust","description":"Learn the Rust lifetime model.","sidebar":"rust"},"Rust/section6/traits":{"id":"Rust/section6/traits","title":"Defining behavior with Traits","description":"Learn how to bring data structs to life with traits in Rust.","sidebar":"rust"},"Rust/section7/closures":{"id":"Rust/section7/closures","title":"Closure Functions in Rust","description":"Learn how to use function closures.","sidebar":"rust"},"Rust/section7/index":{"id":"Rust/section7/index","title":"Intro to Advanced Rust - Iterators & Closures","description":"Delving deeper into more of the Rust Standard Library\'s extended functionality and more advanced concepts: iterators and closures.","sidebar":"rust"},"Rust/section7/iterators":{"id":"Rust/section7/iterators","title":"Iterators in Rust","description":"Learn how to use iterators in Rust.","sidebar":"rust"},"Rust/section7/macros":{"id":"Rust/section7/macros","title":"Macros in Rust","description":"Learn what macros are and how to create basic macros in Rust.","sidebar":"rust"},"Rust/section8/defining-cargo-config":{"id":"Rust/section8/defining-cargo-config","title":"Reading & Defining `cargo.toml`","description":"Understand how to utilize cargo.","sidebar":"rust"},"Rust/section8/defining-crate-features":{"id":"Rust/section8/defining-crate-features","title":"Features in Cargo","description":"Specify features for crates to utilize.","sidebar":"rust"},"Rust/section8/index":{"id":"Rust/section8/index","title":"Learning Cargo, Rust\u2019s Package Management System & Unit Testing","description":"This next module will focus on understanding the structure of a Rust project. It will be less in the playground environment we\'ve been using and more in a code editor like Visual Studio Code and the terminal.","sidebar":"rust"},"Rust/section8/installing-crate":{"id":"Rust/section8/installing-crate","title":"Installing a cargo crate","description":"Understand how to install a crate and use it in a project.","sidebar":"rust"},"Rust/section8/unit-tests":{"id":"Rust/section8/unit-tests","title":"Basic Unit Testing in Rust","description":"Understand how to unit test your Rust code.","sidebar":"rust"},"Rust/setup/installation":{"id":"Rust/setup/installation","title":"Installing & Setting up a Rust Developer Environment","description":"Installing Rust, and setting up your development environment.","sidebar":"rust"},"Substrate/section1/capstone-project":{"id":"Substrate/section1/capstone-project","title":"Course Capstone Project","description":"Learn the specification of what exactly you will build in this course.","sidebar":"substrate"},"Substrate/section1/index":{"id":"Substrate/section1/index","title":"Why Learn Substrate?","description":"","sidebar":"substrate"},"Substrate/section1/substrate-design":{"id":"Substrate/section1/substrate-design","title":"Substrate\u2019s Design Choices","description":"Learn how the design choices of Substrate were made, and why they matter.","sidebar":"substrate"},"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.","sidebar":"substrate"},"Substrate/section1/what-is-substrate":{"id":"Substrate/section1/what-is-substrate","title":"Substrate - powering Web3","description":"Learn what Substrate it is, and why it\'s the future of building infrastructure in web3.","sidebar":"substrate"},"Substrate/section2/index":{"id":"Substrate/section2/index","title":"Substrate 101 - Overview of the Substrate Framework","description":"","sidebar":"substrate"},"Substrate/section2/substrate-pallets":{"id":"Substrate/section2/substrate-pallets","title":"Pallets","description":"Learn what pallets are, and how they fit into FRAME.","sidebar":"substrate"},"Substrate/section2/substrate-runtime":{"id":"Substrate/section2/substrate-runtime","title":"Runtime & Outer Node","description":"Learn what the runtime in Substrate is, and how it defines a state transition function.","sidebar":"substrate"},"Substrate/section2/substrate-storage":{"id":"Substrate/section2/substrate-storage","title":"Storage","description":"Learn how storage works in Substrate, and how it is crucial for managing state.","sidebar":"substrate"},"Substrate/section3/explore-pallet-template":{"id":"Substrate/section3/explore-pallet-template","title":"Exploring the pallet template","description":"Explore the pallet template, and get a sneak peek into what is to come next!","sidebar":"substrate"},"Substrate/section3/index":{"id":"Substrate/section3/index","title":"Substrate Node Template","description":"","sidebar":"substrate"},"Substrate/section3/install-deps":{"id":"Substrate/section3/install-deps","title":"Install, configure, and run the Node Template","description":"Install the substrate-node-template, which will jump-start the development process!","sidebar":"substrate"},"Substrate/section3/install-explore-frontend":{"id":"Substrate/section3/install-explore-frontend","title":"Installing & exploring the frontend template","description":"Install and explore the frontend template for debugging and developing your pallet."},"Substrate/section3/node-template-tour":{"id":"Substrate/section3/node-template-tour","title":"Node Template Tour & Overview","description":"Get familiar with the Substrate node template and its various components.","sidebar":"substrate"},"Substrate/section4/create-storage-map":{"id":"Substrate/section4/create-storage-map","title":"Creating storage maps","description":"Define and create storage maps for our pallet.","sidebar":"substrate"},"Substrate/section4/events-errors":{"id":"Substrate/section4/events-errors","title":"Events & Errors","description":"Define the necessary events and errors needed for our pallet","sidebar":"substrate"},"Substrate/section4/index":{"id":"Substrate/section4/index","title":"Custom FRAME Pallet","description":"","sidebar":"substrate"},"Substrate/section4/pallet-config":{"id":"Substrate/section4/pallet-config","title":"Adjusting Pallet Config & Runtime Overview","description":"Learn how a pallet\'s configuration works and connects with the runtime.","sidebar":"substrate"},"Substrate/section4/project-structure":{"id":"Substrate/section4/project-structure","title":"lib.rs Structure Deepdive","description":"Learn the structure of the pallet works, including how events, errors, and dispatchable functions fit together.","sidebar":"substrate"},"Substrate/section5/coupling-pallets":{"id":"Substrate/section5/coupling-pallets","title":"Using other pallets","description":"Learn how coupling other pallets can extend your own pallet\'s functionality","sidebar":"substrate"},"Substrate/section5/dispatchable":{"id":"Substrate/section5/dispatchable","title":"Creating dispatchable functions","description":"Creating dispatchable functions for our pallet.","sidebar":"substrate"},"Substrate/section5/index":{"id":"Substrate/section5/index","title":"Building a Custom FRAME Pallet - Making our pallet actionable","description":"With our events, errors, and storage in place within our pallet, we can now move on to making it actionable. This module will focus on dispatchable functions and including other pallets into ours.","sidebar":"substrate"},"Substrate/section5/unit-tests":{"id":"Substrate/section5/unit-tests","title":"Writing unit tests for pallets","description":"Learn how to unit test your pallet.","sidebar":"substrate"},"Substrate/section6/index":{"id":"Substrate/section6/index","title":"Building a Custom FRAME Pallet - Deploying & Testing","description":"With tests and functionality in place, you will now deploy and run your pallet in a real environment.","sidebar":"substrate"},"Substrate/section6/run-node":{"id":"Substrate/section6/run-node","title":"Running the node template","description":"Run and review the output from our custom pallet and node.","sidebar":"substrate"},"Substrate/section6/test-frontend":{"id":"Substrate/section6/test-frontend","title":"Use the frontend-template to test your pallet","description":"Testing our pallet using the frontend-template","sidebar":"substrate"},"Substrate/section6/use-polkadotjs":{"id":"Substrate/section6/use-polkadotjs","title":"Using Polkadot.js to explore your storage","description":"Use Polkadot.js to view events, errors, and storage relating to your node and pallet.","sidebar":"substrate"},"Substrate/section7/blockchain-dev":{"id":"Substrate/section7/blockchain-dev","title":"Thinking in terms of blockchain development","description":"How to think like a runtime developer and avoid common pitfalls.","sidebar":"substrate"},"Substrate/section7/how-to-test-frame":{"id":"Substrate/section7/how-to-test-frame","title":"How to approach testing in FRAME","description":"How to correctly approach testing in FRAME and a deeper look at how to mock types properly.","sidebar":"substrate"},"Substrate/section7/index":{"id":"Substrate/section7/index","title":"Building a Custom FRAME Pallet - Pallet & FRAME best practices","description":"In this module, you will learn more granular details and practices on ensuring your pallets are built using sound practices.","sidebar":"substrate"},"Substrate/section7/runtime-panics":{"id":"Substrate/section7/runtime-panics","title":"Runtime Panics & FRAME Best Practices","description":"Learn how to solidify safe programming practices using Rust error handling to avoid panicking in the runtime.","sidebar":"substrate"},"Substrate/section8/benchmarking":{"id":"Substrate/section8/benchmarking","title":"Introduction to Benchmarking","description":"A basic introduction to benchmarking in FRAME","sidebar":"substrate"},"Substrate/section8/chain-genesis-spec":{"id":"Substrate/section8/chain-genesis-spec","title":"Chain Specification","description":"Learn how basis chain and genesis configurations work in FRAME","sidebar":"substrate"},"Substrate/section8/index":{"id":"Substrate/section8/index","title":"FRAME Deepdive","description":"After learning about how to approach testing and development of a FRAME pallet, this module will touch on a few FRAME concepts.","sidebar":"substrate"},"Substrate/section8/origins-calls":{"id":"Substrate/section8/origins-calls","title":"Origins (Privileged Calls) in FRAME","description":"Learn how origins and calls work in Substrate and FRAME.","sidebar":"substrate"},"Substrate/section8/pallet-coupling":{"id":"Substrate/section8/pallet-coupling","title":"Pallet Coupling","description":"Learn how to couple pallets to each other, and the differences between tight and loose (and when to use which).","sidebar":"substrate"}}}')}}]); \ No newline at end of file diff --git a/assets/js/b2def28e.0187891a.js b/assets/js/b2def28e.0187891a.js deleted file mode 100644 index 0fe62aac9..000000000 --- a/assets/js/b2def28e.0187891a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[8224],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var i=n(7294);function a(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 i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),c=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=c(e.components);return i.createElement(s.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=c(n),d=a,f=p["".concat(s,".").concat(d)]||p[d]||m[d]||r;return n?i.createElement(f,o(o({ref:t},u),{},{components:n})):i.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[p]="string"==typeof e?e:a,o[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var i=n(7462),a=(n(7294),n(3905));const r={id:"pallet-config",title:"Adjusting Pallet Config & Runtime Overview",sidebar_label:"Adjusting Pallet Config & Runtime Overview",description:"Learn how a pallet's configuration works and connects with the runtime."},o=void 0,l={unversionedId:"Substrate/section4/pallet-config",id:"Substrate/section4/pallet-config",title:"Adjusting Pallet Config & Runtime Overview",description:"Learn how a pallet's configuration works and connects with the runtime.",source:"@site/docs/Substrate/section4/pallet-config.md",sourceDirName:"Substrate/section4",slug:"/Substrate/section4/pallet-config",permalink:"/docs/Substrate/section4/pallet-config",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section4/pallet-config.md",tags:[],version:"current",lastUpdatedBy:"Bader Youssef",lastUpdatedAt:1687398313,formattedLastUpdatedAt:"Jun 22, 2023",frontMatter:{id:"pallet-config",title:"Adjusting Pallet Config & Runtime Overview",sidebar_label:"Adjusting Pallet Config & Runtime Overview",description:"Learn how a pallet's configuration works and connects with the runtime."},sidebar:"substrate",previous:{title:"Intro to Building a Custom FRAME Pallet",permalink:"/docs/Substrate/section4/"},next:{title:"lib.rs Structure Deepdive",permalink:"/docs/Substrate/section4/project-structure"}},s={},c=[{value:"Adjusting pallet_connect's Config",id:"adjusting-pallet_connects-config",level:2},{value:"runtime/lib.rs Overview",id:"runtimelibrs-overview",level:2},{value:"impl frame_system::Config for Runtime and struct Runtime",id:"impl-frame_systemconfig-for-runtime-and-struct-runtime",level:3},{value:"Pallet Configurations",id:"pallet-configurations",level:3},{value:"construct_runtime! macro",id:"construct_runtime-macro",level:3}],u={toc:c},p="wrapper";function m(e){let{components:t,...n}=e;return(0,a.kt)(p,(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Before looking at our pallet (",(0,a.kt)("inlineCode",{parentName:"p"},"pallet_connect"),"), let's tour the node's runtime - ",(0,a.kt)("inlineCode",{parentName:"p"},"runtime/lib.rs")," and change our pallet's configuration."),(0,a.kt)("h2",{id:"adjusting-pallet_connects-config"},"Adjusting ",(0,a.kt)("inlineCode",{parentName:"h2"},"pallet_connect"),"'s Config"),(0,a.kt)("p",null,"Upon looking at the file, it is evident that each pallet must be configured. ",(0,a.kt)("inlineCode",{parentName:"p"},"pallet_connect")," is no different and requires several configuration parameters pertaining to its functionality: "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"/// Configure pallet-connect\nimpl pallet_connect::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type WeightInfo = pallet_connect::weights::SubstrateWeight;\n type Currency = Balances;\n type MaxBioLength = ConstU32<100>;\n type MinimumLockableAmount = MinimumLockableAmount;\n type MaxNameLength = ConstU32<100>;\n type Randomness = InsecureRandomness;\n}\n")),(0,a.kt)("p",null,"The ones to pay attention to for now are ",(0,a.kt)("inlineCode",{parentName:"p"},"MaxBioLength")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"MaxNameLength"),". These define bounded data types in the pallet and are configurable via the runtime. "),(0,a.kt)("p",null,"Go ahead and change ",(0,a.kt)("inlineCode",{parentName:"p"},"MaxBioLength")," to a higher limit:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"/// Configure pallet-connect\nimpl pallet_connect::Config for Runtime {\n ...\n // Increase from 100 to 200.\n type MaxBioLength = ConstU32<200>;\n ...\n}\n")),(0,a.kt)("p",null,"Congratulations, you just changed your first runtime configuration parameter!"),(0,a.kt)("h2",{id:"runtimelibrs-overview"},(0,a.kt)("inlineCode",{parentName:"h2"},"runtime/lib.rs")," Overview"),(0,a.kt)("p",null,"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."),(0,a.kt)("p",null,"The runtime houses the configuration for not only itself but also the pallets that it utilizes. "),(0,a.kt)("h3",{id:"impl-frame_systemconfig-for-runtime-and-struct-runtime"},(0,a.kt)("inlineCode",{parentName:"h3"},"impl frame_system::Config for Runtime")," and ",(0,a.kt)("inlineCode",{parentName:"h3"},"struct Runtime")),(0,a.kt)("p",null,"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 ",(0,a.kt)("inlineCode",{parentName:"p"},"AccountId")," is defined as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"/// Some way of identifying an account on the chain. We intentionally make it equivalent\n/// to the public key of our transaction signing scheme.\npub type AccountId = <::Signer as IdentifyAccount>::AccountId;\n\nimpl frame_system::Config for Runtime {\n ...\n /// The identifier used to distinguish between accounts.\n type AccountId = AccountId;\n ...\n}\n")),(0,a.kt)("p",null,"Note that the common type is often defined via a type alias at the top of ",(0,a.kt)("inlineCode",{parentName:"p"},"runtime/lib.rs"),"."),(0,a.kt)("h3",{id:"pallet-configurations"},"Pallet Configurations"),(0,a.kt)("p",null,"Following the primary configuration of the overall runtime, it is also necessary to configure each pallet. This is done by implementing the pallet's ",(0,a.kt)("inlineCode",{parentName:"p"},"Config")," trait for the runtime. For example, the sudo pallet (",(0,a.kt)("inlineCode",{parentName:"p"},"pallet_sudo"),") is a simple configuration where it simply utilizes the runtime's ",(0,a.kt)("inlineCode",{parentName:"p"},"Event")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"Call")," types:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"impl pallet_sudo::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n}\n")),(0,a.kt)("h3",{id:"construct_runtime-macro"},(0,a.kt)("inlineCode",{parentName:"h3"},"construct_runtime!")," macro"),(0,a.kt)("p",null,"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, ",(0,a.kt)("inlineCode",{parentName:"p"},"Runtime"),"):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"construct_runtime!(\n pub struct Runtime\n where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic,\n {\n System: frame_system,\n InsecureRandomness: pallet_insecure_randomness_collective_flip,\n Timestamp: pallet_timestamp,\n Aura: pallet_aura,\n Grandpa: pallet_grandpa,\n Balances: pallet_balances,\n TransactionPayment: pallet_transaction_payment,\n Sudo: pallet_sudo,\n // Include the custom logic from the pallet-connect in the runtime.\n Connect: pallet_connect,\n }\n);\n")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/b2def28e.35cc8a82.js b/assets/js/b2def28e.35cc8a82.js new file mode 100644 index 000000000..cb5d7683a --- /dev/null +++ b/assets/js/b2def28e.35cc8a82.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkw3f_education=self.webpackChunkw3f_education||[]).push([[8224],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var i=n(7294);function a(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 i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=i.createContext({}),c=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=c(e.components);return i.createElement(s.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=c(n),d=a,f=p["".concat(s,".").concat(d)]||p[d]||m[d]||r;return n?i.createElement(f,o(o({ref:t},u),{},{components:n})):i.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[p]="string"==typeof e?e:a,o[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var i=n(7462),a=(n(7294),n(3905));const r={id:"pallet-config",title:"Adjusting Pallet Config & Runtime Overview",sidebar_label:"Adjusting Pallet Config & Runtime Overview",description:"Learn how a pallet's configuration works and connects with the runtime."},o=void 0,l={unversionedId:"Substrate/section4/pallet-config",id:"Substrate/section4/pallet-config",title:"Adjusting Pallet Config & Runtime Overview",description:"Learn how a pallet's configuration works and connects with the runtime.",source:"@site/docs/Substrate/section4/pallet-config.md",sourceDirName:"Substrate/section4",slug:"/Substrate/section4/pallet-config",permalink:"/docs/Substrate/section4/pallet-config",draft:!1,editUrl:"https://github.com/w3f/w3f-education/edit/main/docs/Substrate/section4/pallet-config.md",tags:[],version:"current",lastUpdatedBy:"Bader Youssef",lastUpdatedAt:1687398313,formattedLastUpdatedAt:"Jun 22, 2023",frontMatter:{id:"pallet-config",title:"Adjusting Pallet Config & Runtime Overview",sidebar_label:"Adjusting Pallet Config & Runtime Overview",description:"Learn how a pallet's configuration works and connects with the runtime."},sidebar:"substrate",previous:{title:"Custom FRAME Pallet",permalink:"/docs/Substrate/section4/"},next:{title:"lib.rs Structure Deepdive",permalink:"/docs/Substrate/section4/project-structure"}},s={},c=[{value:"Adjusting pallet_connect's Config",id:"adjusting-pallet_connects-config",level:2},{value:"runtime/lib.rs Overview",id:"runtimelibrs-overview",level:2},{value:"impl frame_system::Config for Runtime and struct Runtime",id:"impl-frame_systemconfig-for-runtime-and-struct-runtime",level:3},{value:"Pallet Configurations",id:"pallet-configurations",level:3},{value:"construct_runtime! macro",id:"construct_runtime-macro",level:3}],u={toc:c},p="wrapper";function m(e){let{components:t,...n}=e;return(0,a.kt)(p,(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Before looking at our pallet (",(0,a.kt)("inlineCode",{parentName:"p"},"pallet_connect"),"), let's tour the node's runtime - ",(0,a.kt)("inlineCode",{parentName:"p"},"runtime/lib.rs")," and change our pallet's configuration."),(0,a.kt)("h2",{id:"adjusting-pallet_connects-config"},"Adjusting ",(0,a.kt)("inlineCode",{parentName:"h2"},"pallet_connect"),"'s Config"),(0,a.kt)("p",null,"Upon looking at the file, it is evident that each pallet must be configured. ",(0,a.kt)("inlineCode",{parentName:"p"},"pallet_connect")," is no different and requires several configuration parameters pertaining to its functionality: "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"/// Configure pallet-connect\nimpl pallet_connect::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type WeightInfo = pallet_connect::weights::SubstrateWeight;\n type Currency = Balances;\n type MaxBioLength = ConstU32<100>;\n type MinimumLockableAmount = MinimumLockableAmount;\n type MaxNameLength = ConstU32<100>;\n type Randomness = InsecureRandomness;\n}\n")),(0,a.kt)("p",null,"The ones to pay attention to for now are ",(0,a.kt)("inlineCode",{parentName:"p"},"MaxBioLength")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"MaxNameLength"),". These define bounded data types in the pallet and are configurable via the runtime. "),(0,a.kt)("p",null,"Go ahead and change ",(0,a.kt)("inlineCode",{parentName:"p"},"MaxBioLength")," to a higher limit:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"/// Configure pallet-connect\nimpl pallet_connect::Config for Runtime {\n ...\n // Increase from 100 to 200.\n type MaxBioLength = ConstU32<200>;\n ...\n}\n")),(0,a.kt)("p",null,"Congratulations, you just changed your first runtime configuration parameter!"),(0,a.kt)("h2",{id:"runtimelibrs-overview"},(0,a.kt)("inlineCode",{parentName:"h2"},"runtime/lib.rs")," Overview"),(0,a.kt)("p",null,"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."),(0,a.kt)("p",null,"The runtime houses the configuration for not only itself but also the pallets that it utilizes. "),(0,a.kt)("h3",{id:"impl-frame_systemconfig-for-runtime-and-struct-runtime"},(0,a.kt)("inlineCode",{parentName:"h3"},"impl frame_system::Config for Runtime")," and ",(0,a.kt)("inlineCode",{parentName:"h3"},"struct Runtime")),(0,a.kt)("p",null,"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 ",(0,a.kt)("inlineCode",{parentName:"p"},"AccountId")," is defined as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"/// Some way of identifying an account on the chain. We intentionally make it equivalent\n/// to the public key of our transaction signing scheme.\npub type AccountId = <::Signer as IdentifyAccount>::AccountId;\n\nimpl frame_system::Config for Runtime {\n ...\n /// The identifier used to distinguish between accounts.\n type AccountId = AccountId;\n ...\n}\n")),(0,a.kt)("p",null,"Note that the common type is often defined via a type alias at the top of ",(0,a.kt)("inlineCode",{parentName:"p"},"runtime/lib.rs"),"."),(0,a.kt)("h3",{id:"pallet-configurations"},"Pallet Configurations"),(0,a.kt)("p",null,"Following the primary configuration of the overall runtime, it is also necessary to configure each pallet. This is done by implementing the pallet's ",(0,a.kt)("inlineCode",{parentName:"p"},"Config")," trait for the runtime. For example, the sudo pallet (",(0,a.kt)("inlineCode",{parentName:"p"},"pallet_sudo"),") is a simple configuration where it simply utilizes the runtime's ",(0,a.kt)("inlineCode",{parentName:"p"},"Event")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"Call")," types:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"impl pallet_sudo::Config for Runtime {\n type RuntimeEvent = RuntimeEvent;\n type RuntimeCall = RuntimeCall;\n}\n")),(0,a.kt)("h3",{id:"construct_runtime-macro"},(0,a.kt)("inlineCode",{parentName:"h3"},"construct_runtime!")," macro"),(0,a.kt)("p",null,"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, ",(0,a.kt)("inlineCode",{parentName:"p"},"Runtime"),"):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"construct_runtime!(\n pub struct Runtime\n where\n Block = Block,\n NodeBlock = opaque::Block,\n UncheckedExtrinsic = UncheckedExtrinsic,\n {\n System: frame_system,\n InsecureRandomness: pallet_insecure_randomness_collective_flip,\n Timestamp: pallet_timestamp,\n Aura: pallet_aura,\n Grandpa: pallet_grandpa,\n Balances: pallet_balances,\n TransactionPayment: pallet_transaction_payment,\n Sudo: pallet_sudo,\n // Include the custom logic from the pallet-connect in the runtime.\n Connect: pallet_connect,\n }\n);\n")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.e73eadff.js b/assets/js/runtime~main.3bda2ddc.js similarity index 98% rename from assets/js/runtime~main.e73eadff.js rename to assets/js/runtime~main.3bda2ddc.js index adeea46b5..5cbc66333 100644 --- a/assets/js/runtime~main.e73eadff.js +++ b/assets/js/runtime~main.3bda2ddc.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:"f0309436",79:"23b243c0",162:"7d0e04c4",166:"230518d5",240:"3e14b03e",292:"96733acd",437:"10355f85",533:"b9a79106",572:"ca19872f",644:"f008f3c4",682:"cee1ae93",724:"4c3e973f",898:"f080747e",901:"6e991101",927:"3f1c5cec",961:"d98cd4fb",1004:"5377e133",1121:"029fdf5c",1158:"ab1ac3a8",1209:"c2442da1",1244:"9cab1c67",1309:"b030fd55",1413:"871b6529",1477:"738cd221",1516:"d9ddd61b",1554:"0f3cb241",1578:"416ae14a",1621:"d036134b",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:"c93a5d2c",5221:"770def6a",5225:"f1220b02",5276:"b588e433",5338:"dc4f8022",5372:"210abec6",5465:"0711bfa1",5466:"e7af8c5b",5502:"640c35a7",5599:"b112a5d0",5605:"e70a84e8",5644:"dfd6d9f4",5658:"c0238936",5691:"e840285e",5701:"71837a23",5738:"d8598f6f",5864:"47990fc1",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:"f5251aa4",6845:"758ee7dd",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:"0187891a",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:"eddb5f33",9594:"0034759d",9616:"bdf60722",9671:"ae157a10",9674:"dfe8f48d",9695:"0f3e17a7",9713:"2821960d",9844:"61149943",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:"bc159c19",45:"df2f4449",53:"7a1dcd1f",79:"23b243c0",162:"7d0e04c4",166:"230518d5",240:"3e14b03e",292:"96733acd",437:"10355f85",533:"b9a79106",572:"ca19872f",644:"f008f3c4",682:"cee1ae93",724:"4c3e973f",898:"f080747e",901:"6e991101",927:"3f1c5cec",961:"d98cd4fb",1004:"5377e133",1121:"029fdf5c",1158:"ab1ac3a8",1209:"c2442da1",1244:"9cab1c67",1309:"b030fd55",1413:"871b6529",1477:"738cd221",1516:"d9ddd61b",1554:"0f3cb241",1578:"416ae14a",1621:"d036134b",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:"c93a5d2c",5221:"770def6a",5225:"f1220b02",5276:"b588e433",5338:"dc4f8022",5372:"210abec6",5465:"0711bfa1",5466:"e7af8c5b",5502:"640c35a7",5599:"b112a5d0",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:"f5251aa4",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:"eddb5f33",9594:"0034759d",9616:"bdf60722",9671:"ae157a10",9674:"dfe8f48d",9695:"0f3e17a7",9713:"2821960d",9844:"61149943",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 39cdf8838..458aa2cc8 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 1df82df2c..deed02fcc 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 7f9da71c9..fd4de382b 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 7a08a2147..cbb4e6d58 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 09281cae6..38de237a9 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 d8c7e18ef..4b956f95d 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 cb7bea68b..a14e7e79e 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 12c2755d6..3b2733ebb 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 246462cbc..6e75e8146 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 4509b3ee6..d83b21025 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 7f19c60b7..0e243c03b 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 71a2dd064..d0d13a9bc 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 43f909802..7d0c22cc5 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 dfcfff900..4b467166d 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 5a5fbf573..8c48dcaa5 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 addb20535..549f4b6bb 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 3f18841a4..a5c37c13c 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 8542f02e0..3db93bf02 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 7f3501477..9438506c9 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 28b3c8df9..e14df9175 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 92a33377b..ceee78de7 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 59e5a1cca..4d49ec8a8 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.

- + \ No newline at end of file diff --git a/docs/Blockchain/Module1/bitcoin.html b/docs/Blockchain/Module1/bitcoin.html index 780344ec2..e0a3732db 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 e13f105cf..84f9e6ac3 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 3279d5e44..51cbb4cb3 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 fe181ee5b..926128859 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 95c85c713..dcfdd397f 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 c9ccd4623..d54e709fb 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 4485bcda8..4be2456cc 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 46224a366..80fe6c5ec 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 bbb6f9c9f..4aeb9dde3 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 86fd551d0..a2c80304a 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 50317237e..96799040a 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 cebc746f4..882bff873 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 cdba235e6..5b0c8d493 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 563237ba9..1494b518a 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 3aaff09a9..90c152156 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 23176aaeb..ddd31e98e 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 ff6606d4b..d67fa8ac1 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 ea463e9c1..b6285ee78 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 9361db3ae..7650e415c 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 ea285f7cf..aa69da6c6 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 10b3b0f54..e22dc2e35 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 cde90820c..268774572 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 4d24ae3c2..3fd18bbb9 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 645205c75..52aca2b00 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 2f020fb57..a45e48c34 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 6e4483bb5..904a4063d 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 df4aba004..4a114ae6c 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 a2a240ecd..fa99bc6ac 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 de7bbfde1..9ba9ad148 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 c3598f20b..42d3db318 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 89bf9a05c..47ae5400b 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 a241bf3e2..d75c31cf1 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 cd535363e..dc3bf36f5 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 e642dbb41..808496ee0 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 61dae9a05..b25201575 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 84c3742b7..a18669574 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 6c17d192c..49d0e1f09 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 584ce07ab..caab465be 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 8fa78b72a..24c21397b 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 7449d8fd3..4574dc0e6 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 88f29f1ff..ee3d4f0f8 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 e5cad4360..6f3dd9a3f 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 97fd79852..e63db122b 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 cd2452ade..35ebd0eb9 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 4cffcd5d3..786bf2b2c 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 00fcd2c9d..91ba3e2eb 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 c3456005d..61f074278 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 e79402057..364fb9c1a 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 e255d0125..be1f39e6e 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 f3e8902eb..3e7587a94 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 eaa00ce45..48beae7d8 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 fba94b4a7..823730bdb 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 71f5f3fbc..3cd85bfa7 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 c2f7b656c..76d62a485 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 8886f30af..36e0c094a 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 7f735f629..53229ea4c 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 a227f2b5e..33f78edfe 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 37825b9cb..d5333b928 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 e5674065e..fcf7f6df0 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 a131d5500..d6586eac6 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 c5c0c6b68..3c087cc6a 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 7e88b6a35..5e5a51f7d 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 0248194ca..149ef9579 100644 --- a/docs/Polkadot/Module2/explorenetwork.html +++ b/docs/Polkadot/Module2/explorenetwork.html @@ -5,7 +5,7 @@ Exploring Polkadot Network | Polkadot Education Initiative - + @@ -64,7 +64,7 @@ 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.

- + \ No newline at end of file diff --git a/docs/Polkadot/Module2/governance.html b/docs/Polkadot/Module2/governance.html index ea46781dd..44723aacb 100644 --- a/docs/Polkadot/Module2/governance.html +++ b/docs/Polkadot/Module2/governance.html @@ -5,7 +5,7 @@ On-Chain Governance | Polkadot Education Initiative - + @@ -38,7 +38,7 @@ 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.

- + \ No newline at end of file diff --git a/docs/Polkadot/Module2/treasury.html b/docs/Polkadot/Module2/treasury.html index c65d3a169..edf1e0b38 100644 --- a/docs/Polkadot/Module2/treasury.html +++ b/docs/Polkadot/Module2/treasury.html @@ -5,7 +5,7 @@ Treasury | Polkadot Education Initiative - + @@ -26,7 +26,7 @@ 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

- + \ No newline at end of file diff --git a/docs/Polkadot/Module3/consensus.html b/docs/Polkadot/Module3/consensus.html index 1d2ce9cce..66fa81757 100644 --- a/docs/Polkadot/Module3/consensus.html +++ b/docs/Polkadot/Module3/consensus.html @@ -5,7 +5,7 @@ Polkadot Network Consensus | Polkadot Education Initiative - + @@ -60,7 +60,7 @@ 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

- + \ No newline at end of file diff --git a/docs/Polkadot/Module3/maintainers.html b/docs/Polkadot/Module3/maintainers.html index c28ebd834..f1fa6be48 100644 --- a/docs/Polkadot/Module3/maintainers.html +++ b/docs/Polkadot/Module3/maintainers.html @@ -5,7 +5,7 @@ Validators and Nominators | Polkadot Education Initiative - + @@ -42,7 +42,7 @@ 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.

- + \ No newline at end of file diff --git a/docs/Polkadot/Module3/npos.html b/docs/Polkadot/Module3/npos.html index d2d16d5cd..4ff8954a8 100644 --- a/docs/Polkadot/Module3/npos.html +++ b/docs/Polkadot/Module3/npos.html @@ -5,7 +5,7 @@ Nominated Proof of Staking | Polkadot Education Initiative - + @@ -51,7 +51,7 @@ 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)

- + \ No newline at end of file diff --git a/docs/Polkadot/Module3/securityimprovements.html b/docs/Polkadot/Module3/securityimprovements.html index a5c06b3f1..c365a5f1a 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 ddf55bee6..ec75ef905 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 21853b6ad..c9d729730 100644 --- a/docs/Polkadot/Module4/cryptography.html +++ b/docs/Polkadot/Module4/cryptography.html @@ -5,7 +5,7 @@ Cryptography | Polkadot Education Initiative - + @@ -53,7 +53,7 @@ pointless slashing. We impose no prior restrictions on the cryptography employed by specific components or their 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 8a2c7af72..8da42e7a0 100644 --- a/docs/Polkadot/Module4/decentralization.html +++ b/docs/Polkadot/Module4/decentralization.html @@ -5,7 +5,7 @@ Decentralization of Network | Polkadot Education Initiative - + @@ -24,7 +24,7 @@ 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

- + \ No newline at end of file diff --git a/docs/Polkadot/Module4/networking.html b/docs/Polkadot/Module4/networking.html index e42d1694e..63060ba63 100644 --- a/docs/Polkadot/Module4/networking.html +++ b/docs/Polkadot/Module4/networking.html @@ -5,7 +5,7 @@ Networking | Polkadot Education Initiative - + @@ -54,7 +54,7 @@ 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

- + \ No newline at end of file diff --git a/docs/Polkadot/Module4/nodes.html b/docs/Polkadot/Module4/nodes.html index e25093062..41b3d23ee 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 fa648dcad..f1d7d76e7 100644 --- a/docs/Polkadot/Module4/parachainblock.html +++ b/docs/Polkadot/Module4/parachainblock.html @@ -5,7 +5,7 @@ Path of a Parachain Block | Polkadot Education Initiative - + @@ -55,7 +55,7 @@ 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.

- + \ No newline at end of file diff --git a/docs/Polkadot/Module5/architectureimprovements.html b/docs/Polkadot/Module5/architectureimprovements.html index b6448e5d3..81917606c 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 1eccc1107..d57339c4d 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 64a0ab171..ce34588bf 100644 --- a/docs/Polkadot/Module5/interoperability.html +++ b/docs/Polkadot/Module5/interoperability.html @@ -5,7 +5,7 @@ Interoperability | Polkadot Education Initiative - + @@ -40,7 +40,7 @@ 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.

- + \ No newline at end of file diff --git a/docs/Polkadot/Module5/parachains.html b/docs/Polkadot/Module5/parachains.html index ad65f6ae7..c7b43ec1a 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 9332b4d9a..47d8d63df 100644 --- a/docs/Polkadot/Module5/scalability.html +++ b/docs/Polkadot/Module5/scalability.html @@ -5,7 +5,7 @@ Scalability | Polkadot Education Initiative - + @@ -45,7 +45,7 @@ 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.

- + \ No newline at end of file diff --git a/docs/Polkadot/Module6/developers.html b/docs/Polkadot/Module6/developers.html index 50e09b855..7f4cacab9 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 890ce0a96..c00bcf7cb 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 40d93a20d..eeec98afb 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 246ad22a3..0f4a23222 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 9682cdf88..8a64d4004 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 1c14ee2e2..dc19e5442 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 042fcefdd..d04f9ab7f 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 344499508..1ad92a2ba 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 8d73b022f..8a203700f 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 cb3397757..e575908b0 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 d8b671e5c..2ffa03584 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 8f1b7a9f9..d19c98160 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 5d6fb09cd..6ed948208 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 ce7a0f449..a90916e92 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 26c246ab0..539a0efb8 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 e7b7c53cb..1dc9f58f3 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 aea351709..8c0042ab1 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 f54eeea80..1ad986120 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 538524df6..218925c79 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 28eccc144..a62503572 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 - + @@ -20,7 +20,7 @@ Display Debug Default

- + \ No newline at end of file diff --git a/docs/Rust/section3/ownership.html b/docs/Rust/section3/ownership.html index 60dc9d221..7c644bafd 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 036a67758..76c371d8c 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 87b290636..76f5363f5 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 802d5f78c..4591e34e9 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 a279cd3cf..636dc88fb 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.

- + \ No newline at end of file diff --git a/docs/Rust/section4/panic.html b/docs/Rust/section4/panic.html index c2870e0b7..a8358516d 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 da118f574..1bc1b891e 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 760148176..43e0e0a68 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 21132863a..2fc6433c2 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 14c3d44d4..bab424de7 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 8587556e8..68f845d24 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 163e197b5..c06c7b813 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 834646ada..44e842dad 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 92570fa10..c653156da 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 e1da10931..db94834df 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 bad467771..51a07e7f6 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 ab714de68..4a85f32ad 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 64bcae2f9..34885ac9c 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 1d9c271ab..ad78a1efc 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 5cb0aa60f..80b50cf88 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.
- + \ No newline at end of file diff --git a/docs/Rust/section8.html b/docs/Rust/section8.html index da6834cbd..98ab1fd40 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 312c38018..08ddef5f3 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 22e896328..6dc0806c4 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 8fd2a0fdd..8560a481a 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 3b3e7b342..a51c8ccdb 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 68657e8b2..e863e4074 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 d3912f68b..e05570429 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 79a9f052f..04839966d 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 9e9ddda54..539971774 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 0b68285ce..bf97a9736 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.

- + \ 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 d56a8d06c..517b99e68 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 8232a8c06..af5bf184e 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 c419b6d00..85ec13160 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 3b57a339a..b2c6975d7 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 957585111..ac0ad0737 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 789f1875c..f954b5de1 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 64d9aeb4a..ff6e5e5f9 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.

- +

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 f9e48012c..c21da40b6 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 651f150a5..cdddc0762 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 8ec2bd632..fda32a19d 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 aab9e2d12..95d149372 100644 --- a/docs/Substrate/section4.html +++ b/docs/Substrate/section4.html @@ -3,15 +3,15 @@ -Intro to Building a Custom FRAME Pallet | Polkadot Education Initiative +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 3664070b0..be5478f0c 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 5939686f6..2a171f7e1 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 aecb19aaf..426568d52 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,
}
);
- +

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 2b875c6b7..ea05c3a3c 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 ad18eb104..c7c794995 100644 --- a/docs/Substrate/section5.html +++ b/docs/Substrate/section5.html @@ -5,13 +5,13 @@ Building a Custom FRAME Pallet - Making our pallet actionable | 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 a19daf3f2..ff5bb108e 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 499792861..d3f7f787c 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(())
}
- + \ No newline at end of file diff --git a/docs/Substrate/section5/unit-tests.html b/docs/Substrate/section5/unit-tests.html index 7811a4ad4..56d674cc0 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 579df0252..da09200ca 100644 --- a/docs/Substrate/section6.html +++ b/docs/Substrate/section6.html @@ -5,13 +5,13 @@ Building a Custom FRAME Pallet - Deploying & Testing | 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 ae4e2de2f..15ee134fe 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 2c3a815cb..b0f14d341 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 c15e26ce2..f3b703f9d 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 ff23a2c4d..871ceef26 100644 --- a/docs/Substrate/section7.html +++ b/docs/Substrate/section7.html @@ -5,13 +5,13 @@ Building a Custom FRAME Pallet - Pallet & 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 d45275063..b29689679 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 fae1d964b..5d25b42c8 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 1cdc179d2..089ffa25c 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 2e094c003..91c7c4d81 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 612a4d464..0f2b8e186 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 8313859bf..4ac26fe6a 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 7d323cc62..29d6b8d89 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 575b67a94..6a284ba1a 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 a777444d8..1487b97e8 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 cc6394f16..f4c754a29 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 e5ea1c500..8ebc92d59 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 426a0cac0..b68d538c0 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 0651a9c5c..f0db54482 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 04a643c0c..9e39743ab 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 a3f78ade8..9d7331f96 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 d8107c8f0..d2a942e49 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 f50e18ba5..477d4863f 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 81f1199fb..1a7258972 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