From 80378e067db5eb8bd95a2ab68ba3fc0677a99e67 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Thu, 19 Sep 2019 18:49:50 +0200 Subject: [PATCH 01/23] update contribution guidelines to align with new doc guidelines --- .gitignore | 3 +-- CONTRIBUTING.md | 48 +++++++++++++++++++++++++--------- src/SUMMARY.md | 26 +++++++++--------- src/{misc => base}/README.md | 2 +- src/{misc => base}/dessert.md | 0 src/{misc => base}/resource.md | 0 src/{misc => base}/setup.md | 0 src/{misc => base}/template.md | 0 8 files changed, 50 insertions(+), 29 deletions(-) rename src/{misc => base}/README.md (89%) rename src/{misc => base}/dessert.md (100%) rename src/{misc => base}/resource.md (100%) rename src/{misc => base}/setup.md (100%) rename src/{misc => base}/template.md (100%) diff --git a/.gitignore b/.gitignore index d210eaa95..8283b4ad1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ # todos are scattered in these files k.md # to be made into a larger PR for Substrate soon -vision.md -todo.md +v002.md **/*/drafts/ **/*/*/drafts/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6b1d4c8c8..d8eabc77b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,23 +1,38 @@ # Contributing Guidelines -**Recipes are not entire tutorials -- they are small patterns that may be extracted from tutorials**. The purpose of Substrate Cookbook is to identify best practices in Substrate runtime code and extract patterns that are useful outside of the context of the specific use case. - -1. isolate specific pattern -2. walk through logic in piecewise steps -3. show/link to the full file in the used codebase +The **purpose** of [Substrate Recipes](https://substrate.dev/recipes/) is to identify best practices in Substrate runtime code and extract useful patterns. -If you want to get involved, feel free to open an [issue](https://github.com/substrate-developer-hub/recipes/issues/new) with any ideas/comments/questions.**The markdown for each recipe can be found by following the paths set in [SUMMARY.md](./src/SUMMARY.md)**. +* [Scope and Structure](#scope) +* [Getting Involved](#involve) +* [Mdbook Local Build Instructions](#instructions) +* [Etiquette](#etiquette) -I'm going to spend more time working on samples over the next few weeks, but I'm in the process of improving this project as well. +## Scope and Structure -## Common Etiquette +At the moment, the recipes onboards developers by focusing primarily on **module development** patterns before reusing these patterns in the context of **runtime configuration** (runtime section is *in-progress*). -* try to not use "we" or "our" because it often is conducive to unnecessary language -* frequently link to outside content ie original code, blog/tutorial references, documentation for a specific method/trait/etc +The **[kitchen](./kitchen)** contains code from the recipes in the context of modules/runtimes, while **[src](./src)** houses the commentary for related explanations and references. The structure of the `src` can be found in [src/SUMMARY.md](./src/SUMMARY.md). + +In practice, the recipes supplements existing resources by providing usage examples, demonstrating best practices in context, and extending simple samples/tutorials. Likewise, it is necessary to **frequently link to/from [reference docs](https://crates.parity.io/substrate_service/index.html?search=srml), [tutorials](https://github.com/substrate-developer-hub/), and [high-level docs](https://substrate.dev/)**. + +The recipes do NOT cover: +* module testing +* rpc, cli, and other [`node/`](https://github.com/paritytech/substrate/tree/master/node) stuff outside the runtime +* frontend UI +* protocol engineering (consensus, networking, etc.) + +If you're interested in adding new chapters for any of these sections, [create an issue](https://github.com/substrate-developer-hub/recipes/issues/new) and convince us :) + +## Getting Involved -## Local Build Instructions +1. isolate useful pattern (in [`issues`](https://github.com/substrate-developer-hub/recipes/issues)) +2. build a module/runtime example with context (in [`kitchen`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen)) +3. walk through logic of useful pattern in piecewise steps (in [`src/`](https://github.com/substrate-developer-hub/recipes/tree/master/src)) +4. link `src` and `kitchen` (in [`src/`](https://github.com/substrate-developer-hub/recipes/tree/master/src)) -Stage locally before making a PR. Don't forget to switch to a new branch before you make edits. +## Local Build Instructions + +**Stage locally before making a PR.** Don't forget to switch to a new branch before you make edits. 1. install [`mdbook`](https://github.com/rust-lang-nursery/mdBook) @@ -31,4 +46,11 @@ $ cargo install mdbook $ mdbook build --open ``` -3. If everything looks good, open a [Pull Request](https://github.com/substrate-developer-hub/recipes/compare) \ No newline at end of file +3. If everything looks good, open a [Pull Request](https://github.com/substrate-developer-hub/recipes/compare) + +## Etiquette + +* don't use "we" or "our" because it often is conducive to unnecessary language +* use active voice (instead of passive voice ie "you may want to use active voice") +* frequently link to outside content ie original code, blog/tutorial references, documentation for a specific method/trait/etc +* **be nice, abide by the [Rust CoC](https://www.rust-lang.org/policies/code-of-conduct)** \ No newline at end of file diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f449f8a42..683a38b44 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,22 +1,22 @@ # Summary -- [Introduction](./misc/README.md) -- [Getting Started](./misc/setup.md) -- [Events](./event/README.md) - - [Adding Machine](./event/adder.md) -- [Storage](./storage/README.md) - - [Single Value](./storage/value.md) - - [Maps](./storage/list.md) - - [Configurable Constants](./storage/constants.md) +- [Introduction](./base/README.md) +- [Set Up Substrate](./base/setup.md) +- [Rust Review](./base/rust.md) +- [Basic Module Development](./module/README.md) + - [Adding Machine](./module/adder.md) + - [Single Value Storage](./module/value.md) + - [Maps](./module/list.md) + - [Configurable Constants](./module/constants.md) - [Types and Traits](./types/README.md) - [Collateral Management](./types/collateral.md) - - [Nested Structs](./types/structs.md) -- [Design Patterns](./common/README.md) + - [Nested Structs](./types/structs.md) - might get rid of this... + - [Safety Mindset]() - Rust thing but also set the context of module development +- [Extra Design Patterns](./common/README.md) - [Permissioned Methods](./common/permissioned.md) - [Blockchain Event Loop](./common/loop.md) -- [Recipes](./recipes/README.md) - - [Token Transfer](./recipes/token.md) - - [Social Network](./recipes/social.md) + - [Automated Token Issuance](./recipes/token.md) + - [The Social Network](./recipes/social.md) - [SRML Tour](./tour/README.md) - [Treasury](./tour/treasury.md) - [Advanced](./safety/README.md) diff --git a/src/misc/README.md b/src/base/README.md similarity index 89% rename from src/misc/README.md rename to src/base/README.md index e5bba0674..b07ab862a 100644 --- a/src/misc/README.md +++ b/src/base/README.md @@ -1,6 +1,6 @@ # Substrate Recipes 🍴😋🍴 -Substrate Recipes is a collection of simple code patterns that demonstrate best practices when building blockchains with **[Substrate](https://github.com/paritytech/substrate)**. The repo used to build this book is [open source](https://github.com/substrate-developer-hub/recipes) and [open for contributions](https://github.com/substrate-developer-hub/recipes/blob/master/CONTRIBUTING.md). +Substrate Recipes is a collection of simple code patterns that demonstrate best practices when building blockchains with **[Substrate](https://github.com/paritytech/substrate)**. The repo used to build this book is [open source](https://github.com/substrate-developer-hub/recipes). Check out the [contributions guidelines](https://github.com/substrate-developer-hub/recipes/blob/master/CONTRIBUTING.md) for an overview of the structure and directions for getting involved. ## What is Substrate? diff --git a/src/misc/dessert.md b/src/base/dessert.md similarity index 100% rename from src/misc/dessert.md rename to src/base/dessert.md diff --git a/src/misc/resource.md b/src/base/resource.md similarity index 100% rename from src/misc/resource.md rename to src/base/resource.md diff --git a/src/misc/setup.md b/src/base/setup.md similarity index 100% rename from src/misc/setup.md rename to src/base/setup.md diff --git a/src/misc/template.md b/src/base/template.md similarity index 100% rename from src/misc/template.md rename to src/base/template.md From 375df272623d52bb377b80b20844257701a15439 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Thu, 19 Sep 2019 19:22:09 +0200 Subject: [PATCH 02/23] fix build, fix rr --- kitchen/random/src/lib.rs | 21 ++++++++++ src/README.md | 40 +++++++++---------- src/SUMMARY.md | 34 ++++++++-------- src/advanced/random.md | 24 +++++++++++ src/base/rust.md | 3 ++ src/{event => basics}/adder.md | 0 src/{storage => basics}/constants.md | 0 .../README.md => basics/event_readme.md} | 0 src/{storage => basics}/list.md | 0 .../README.md => basics/storage_readme.md} | 0 src/{storage => basics}/value.md | 0 src/{common => examples}/README.md | 0 src/{common => examples}/loop.md | 0 src/{common => examples}/permissioned.md | 0 src/{common => examples}/random.md | 0 .../README.md => examples/recipes_readme.md} | 0 src/{recipes => examples}/social.md | 0 src/{recipes => examples}/token.md | 0 src/safety/README.md | 2 +- src/safety/safemath.md | 7 ++-- src/storage/drafts/enums.md | 2 - 21 files changed, 88 insertions(+), 45 deletions(-) create mode 100644 src/advanced/random.md create mode 100644 src/base/rust.md rename src/{event => basics}/adder.md (100%) rename src/{storage => basics}/constants.md (100%) rename src/{event/README.md => basics/event_readme.md} (100%) rename src/{storage => basics}/list.md (100%) rename src/{storage/README.md => basics/storage_readme.md} (100%) rename src/{storage => basics}/value.md (100%) rename src/{common => examples}/README.md (100%) rename src/{common => examples}/loop.md (100%) rename src/{common => examples}/permissioned.md (100%) rename src/{common => examples}/random.md (100%) rename src/{recipes/README.md => examples/recipes_readme.md} (100%) rename src/{recipes => examples}/social.md (100%) rename src/{recipes => examples}/token.md (100%) delete mode 100644 src/storage/drafts/enums.md diff --git a/kitchen/random/src/lib.rs b/kitchen/random/src/lib.rs index 986afe8b6..456107fc7 100644 --- a/kitchen/random/src/lib.rs +++ b/kitchen/random/src/lib.rs @@ -1,9 +1,15 @@ +/// Generating randomness with weak entropy #![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "128"] use primitives::{ed25519, Blake2Hasher, Hasher, H256}; +use primitives::{Blake2Hasher, Hasher}; /// Generating Randomness example(s) use support::{decl_event, decl_module, decl_storage, dispatch::Result, ensure, StorageValue}; use system::ensure_signed; +use parity_scale_codec::{Encode, Decode}; +use support::{decl_event, decl_module, decl_storage, StorageValue, dispatch::Result}; +use system::{self, ensure_signed}; pub trait Trait: system::Trait { type Event: From + Into<::Event>; @@ -20,8 +26,13 @@ decl_module! { fn deposit_event() = default; fn weak_entropy(origin) -> u32{ + fn use_weak_entropy(origin) -> Result { + let _ = ensure_signed(origin)?; + + let random_seed = >::random_seed(); let nonce = ::get(); let new_random = (>::random_seed(), nonce) + let new_random = (random_seed, nonce) .using_encoded(|b| Blake2Hasher::hash(b)) .using_encoded(|mut b| u64::decode(&mut b)) .expect("Hash must be bigger than 8 bytes; Qed"); @@ -29,6 +40,9 @@ decl_module! { ::put(new_nonce); Self::deposit_event(RawEvent::RNGenerate(new_random)); new_random + Self::deposit_event(Event::WeakEntropy(new_random)); + ::put(nonce + 1); + Ok(()) } } } @@ -36,5 +50,12 @@ decl_module! { decl_event!( pub enum Event { RNGenerate(u32), + WeakEntropy(u64), } ); + +decl_storage! { + trait Store for Module as Ranodom { + Nonce get(nonce): u64; + } +} \ No newline at end of file diff --git a/src/README.md b/src/README.md index f449f8a42..acc3df11c 100644 --- a/src/README.md +++ b/src/README.md @@ -1,29 +1,27 @@ # Summary -- [Introduction](./misc/README.md) -- [Getting Started](./misc/setup.md) -- [Events](./event/README.md) - - [Adding Machine](./event/adder.md) -- [Storage](./storage/README.md) - - [Single Value](./storage/value.md) - - [Maps](./storage/list.md) - - [Configurable Constants](./storage/constants.md) +- [Introduction](./base/README.md) +- [Set Up Substrate](./base/setup.md) +- [Rust Review](./base/rust.md) +- [Module Fundamentals](./basics/README.md) + - [Adding Machine](./basics/adder.md) + - [Single Value Storage](./basics/value.md) + - [Maps](./basics/list.md) + - [Configurable Constants](./basics/constants.md) - [Types and Traits](./types/README.md) - - [Collateral Management](./types/collateral.md) - - [Nested Structs](./types/structs.md) -- [Design Patterns](./common/README.md) - - [Permissioned Methods](./common/permissioned.md) - - [Blockchain Event Loop](./common/loop.md) -- [Recipes](./recipes/README.md) - - [Token Transfer](./recipes/token.md) - - [Social Network](./recipes/social.md) + - [Currency Type Usage](./types/collateral.md) + - [Safety First](./safety/README.md) + - [Robust Path Handling](./safety/paths.md) + - [Fixed Point Arithmetic](./safety/safemath.md) +- [Examples](./examples/README.md) + - [Permissioned Methods](./examples/permissioned.md) + - [Scheduling Runtime Tasks](./examples/loop.md) + - [Automated Token Issuance](./examples/token.md) + - [Social Network](./examples/social.md) - [SRML Tour](./tour/README.md) - [Treasury](./tour/treasury.md) -- [Advanced](./safety/README.md) - - [Declarative Programming](./safety/cop.md) - - [Optimizations](./safety/optimizations.md) -- [Featured Tutorials](./misc/dessert.md) +- [Featured Tutorials](./base/dessert.md) ----------- -[More Resources](./misc/resource.md) \ No newline at end of file +[More Resources](./base/resource.md) \ No newline at end of file diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 683a38b44..acc3df11c 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -3,27 +3,25 @@ - [Introduction](./base/README.md) - [Set Up Substrate](./base/setup.md) - [Rust Review](./base/rust.md) -- [Basic Module Development](./module/README.md) - - [Adding Machine](./module/adder.md) - - [Single Value Storage](./module/value.md) - - [Maps](./module/list.md) - - [Configurable Constants](./module/constants.md) +- [Module Fundamentals](./basics/README.md) + - [Adding Machine](./basics/adder.md) + - [Single Value Storage](./basics/value.md) + - [Maps](./basics/list.md) + - [Configurable Constants](./basics/constants.md) - [Types and Traits](./types/README.md) - - [Collateral Management](./types/collateral.md) - - [Nested Structs](./types/structs.md) - might get rid of this... - - [Safety Mindset]() - Rust thing but also set the context of module development -- [Extra Design Patterns](./common/README.md) - - [Permissioned Methods](./common/permissioned.md) - - [Blockchain Event Loop](./common/loop.md) - - [Automated Token Issuance](./recipes/token.md) - - [The Social Network](./recipes/social.md) + - [Currency Type Usage](./types/collateral.md) + - [Safety First](./safety/README.md) + - [Robust Path Handling](./safety/paths.md) + - [Fixed Point Arithmetic](./safety/safemath.md) +- [Examples](./examples/README.md) + - [Permissioned Methods](./examples/permissioned.md) + - [Scheduling Runtime Tasks](./examples/loop.md) + - [Automated Token Issuance](./examples/token.md) + - [Social Network](./examples/social.md) - [SRML Tour](./tour/README.md) - [Treasury](./tour/treasury.md) -- [Advanced](./safety/README.md) - - [Declarative Programming](./safety/cop.md) - - [Optimizations](./safety/optimizations.md) -- [Featured Tutorials](./misc/dessert.md) +- [Featured Tutorials](./base/dessert.md) ----------- -[More Resources](./misc/resource.md) \ No newline at end of file +[More Resources](./base/resource.md) \ No newline at end of file diff --git a/src/advanced/random.md b/src/advanced/random.md new file mode 100644 index 000000000..803eabcd1 --- /dev/null +++ b/src/advanced/random.md @@ -0,0 +1,24 @@ +# Generating Randomness + +Substrate uses a safe mixing algorithm to generate randomness using the entropy of previous blocks. Because it is dependent on previous blocks, it can take many blocks for the seed to change. + +```rust +let random_seed = >::random_seed(); +``` + +**To increase entropy**, we can introduce a nonce and a user-specified property. This provides us with a basic RNG on Substrate: +```rust +let nonce = ::get(); +let new_random = (>::random_seed(), nonce) + .using_encoded(|b| Blake2Hasher::hash(b)) + .using_encoded(|mut b| u64::decode(&mut b)) + .expect("Hash must be bigger than 8 bytes; Qed"); +>::mutate(|n| *n += 1); +>::put(nonce + 1); +``` + +**also see...** +* [code in kitchen](https://github.com/substrate-developer-hub/recipes/blob/master/kitchen/random/src/lib.rs) +* https://github.com/paritytech/ink/issues/57 + +**[Back to Recipes](https://substrate.dev/recipes/)** \ No newline at end of file diff --git a/src/base/rust.md b/src/base/rust.md new file mode 100644 index 000000000..4d77bd35a --- /dev/null +++ b/src/base/rust.md @@ -0,0 +1,3 @@ +# Intro to Rust + +* TODO \ No newline at end of file diff --git a/src/event/adder.md b/src/basics/adder.md similarity index 100% rename from src/event/adder.md rename to src/basics/adder.md diff --git a/src/storage/constants.md b/src/basics/constants.md similarity index 100% rename from src/storage/constants.md rename to src/basics/constants.md diff --git a/src/event/README.md b/src/basics/event_readme.md similarity index 100% rename from src/event/README.md rename to src/basics/event_readme.md diff --git a/src/storage/list.md b/src/basics/list.md similarity index 100% rename from src/storage/list.md rename to src/basics/list.md diff --git a/src/storage/README.md b/src/basics/storage_readme.md similarity index 100% rename from src/storage/README.md rename to src/basics/storage_readme.md diff --git a/src/storage/value.md b/src/basics/value.md similarity index 100% rename from src/storage/value.md rename to src/basics/value.md diff --git a/src/common/README.md b/src/examples/README.md similarity index 100% rename from src/common/README.md rename to src/examples/README.md diff --git a/src/common/loop.md b/src/examples/loop.md similarity index 100% rename from src/common/loop.md rename to src/examples/loop.md diff --git a/src/common/permissioned.md b/src/examples/permissioned.md similarity index 100% rename from src/common/permissioned.md rename to src/examples/permissioned.md diff --git a/src/common/random.md b/src/examples/random.md similarity index 100% rename from src/common/random.md rename to src/examples/random.md diff --git a/src/recipes/README.md b/src/examples/recipes_readme.md similarity index 100% rename from src/recipes/README.md rename to src/examples/recipes_readme.md diff --git a/src/recipes/social.md b/src/examples/social.md similarity index 100% rename from src/recipes/social.md rename to src/examples/social.md diff --git a/src/recipes/token.md b/src/examples/token.md similarity index 100% rename from src/recipes/token.md rename to src/examples/token.md diff --git a/src/safety/README.md b/src/safety/README.md index 699004ec7..ea0b3ef78 100644 --- a/src/safety/README.md +++ b/src/safety/README.md @@ -1,4 +1,4 @@ -# Safety and Optimization +# Safety First Unlike conventional software development kits that abstract away low-level decisions, Substrate grants developers fine-grain control over the underlying implementation. This approach fosters high-performance, modular applications. At the same time, it also demands increased attention from developers. To quote the [late Uncle Ben](https://knowyourmeme.com/memes/with-great-power-comes-great-responsibility), **with great power comes great responsibility**. diff --git a/src/safety/safemath.md b/src/safety/safemath.md index efcaebff0..df55245ba 100644 --- a/src/safety/safemath.md +++ b/src/safety/safemath.md @@ -1,7 +1,8 @@ -# Safe Arithmetic +# Fixed Point Arithmetic -* computers are notoriously bad with precision with small numbers -* link to the inflation section wherein I will discuss some of this stuff +*computers are notoriously bad with precision with small numbers* + +see https://github.com/substrate-developer-hub/recipes/issues/12 for links ## Perbill and Permill Usage diff --git a/src/storage/drafts/enums.md b/src/storage/drafts/enums.md deleted file mode 100644 index 3763efec6..000000000 --- a/src/storage/drafts/enums.md +++ /dev/null @@ -1,2 +0,0 @@ -# Custom Enums in Polkadot -> [Recipes Link](https://docs.substrate.dev/docs/substrate-runtime-recipes#section-working-with-custom-enums-in-polkadot-apps) \ No newline at end of file From 0300e9a255e6c1695e51f2926603974eece52b63 Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Thu, 19 Sep 2019 19:45:06 +0200 Subject: [PATCH 03/23] Update kitchen/random/src/lib.rs --- kitchen/random/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kitchen/random/src/lib.rs b/kitchen/random/src/lib.rs index 456107fc7..9e59dce65 100644 --- a/kitchen/random/src/lib.rs +++ b/kitchen/random/src/lib.rs @@ -31,7 +31,6 @@ decl_module! { let random_seed = >::random_seed(); let nonce = ::get(); - let new_random = (>::random_seed(), nonce) let new_random = (random_seed, nonce) .using_encoded(|b| Blake2Hasher::hash(b)) .using_encoded(|mut b| u64::decode(&mut b)) @@ -58,4 +57,4 @@ decl_storage! { trait Store for Module as Ranodom { Nonce get(nonce): u64; } -} \ No newline at end of file +} From 6d8dfd47e1f7089c661a78968a0e3b22fbeba4d5 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Fri, 20 Sep 2019 01:38:08 +0200 Subject: [PATCH 04/23] init new structure --- src/.DS_Store | Bin 6148 -> 6148 bytes src/README.md | 16 ++--- src/SUMMARY.md | 15 ++-- src/advanced/random.md | 7 +- src/base/runtime.md | 5 ++ src/dao/README.md | 7 ++ src/{examples => dao}/loop.md | 0 src/{examples => dao}/permissioned.md | 0 src/dao/treasury.md | 95 ++++++++++++++++++++++++++ src/examples/random.md | 25 ------- src/identity/README.md | 3 + src/nft/README.md | 3 + src/proof/README.md | 3 + src/token/README.md | 8 +++ src/token/tcr.md | 0 src/{examples => token}/token.md | 0 16 files changed, 143 insertions(+), 44 deletions(-) create mode 100644 src/base/runtime.md create mode 100644 src/dao/README.md rename src/{examples => dao}/loop.md (100%) rename src/{examples => dao}/permissioned.md (100%) create mode 100644 src/dao/treasury.md delete mode 100644 src/examples/random.md create mode 100644 src/identity/README.md create mode 100644 src/nft/README.md create mode 100644 src/proof/README.md create mode 100644 src/token/README.md create mode 100644 src/token/tcr.md rename src/{examples => token}/token.md (100%) diff --git a/src/.DS_Store b/src/.DS_Store index 62597e2fd4f2856b0a66e31cc8e58f28066b45a9..d4b7570c882fc60c62d3d4102d1306fea2c16809 100644 GIT binary patch delta 86 zcmZoMXfc=|#>B`mu~2NHo}wr#0|Nsi1A_pAXHI@{QcivnkS{Q?a5*FEWHYAqn}wOb luxxCIVBE~k!OsEIwK-D%>g1?m;q}06O;e| delta 156 zcmZoMXfc=|#>B)qu~2NHo}w^20|Nsi1A_nqLy2cjesWSye$wWTj4K(nK@zMCsSIU6 zoQIGEiUQR#tOR0>|6sttFqwyOy*xWZF+&MMK0^^hB11YuD$FpL`ps(?nOQfpbMSKj a?FQQLoq009h$RQmY9L}@*c>3Tg&6?)F(!Wi diff --git a/src/README.md b/src/README.md index acc3df11c..64128067a 100644 --- a/src/README.md +++ b/src/README.md @@ -11,15 +11,15 @@ - [Types and Traits](./types/README.md) - [Currency Type Usage](./types/collateral.md) - [Safety First](./safety/README.md) - - [Robust Path Handling](./safety/paths.md) + - [Declarative Programming](./safety/paths.md) - [Fixed Point Arithmetic](./safety/safemath.md) -- [Examples](./examples/README.md) - - [Permissioned Methods](./examples/permissioned.md) - - [Scheduling Runtime Tasks](./examples/loop.md) - - [Automated Token Issuance](./examples/token.md) - - [Social Network](./examples/social.md) -- [SRML Tour](./tour/README.md) - - [Treasury](./tour/treasury.md) +- [Runtime Configuration](./runtime/README.md) +- [Recipes](./recipes/README.md) + - [Token Curated Registry](./recipes/token.md) + - [NFT Ticket Machine](./recipes/ticket.md) + - [Proof of Existence](./recipes/poe.md) + - [Treasury](./recipes/treasury.md) + - [Social Network](./recipes/social.md) - [Featured Tutorials](./base/dessert.md) ----------- diff --git a/src/SUMMARY.md b/src/SUMMARY.md index acc3df11c..599e2697b 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -11,15 +11,14 @@ - [Types and Traits](./types/README.md) - [Currency Type Usage](./types/collateral.md) - [Safety First](./safety/README.md) - - [Robust Path Handling](./safety/paths.md) + - [Declarative Programming](./safety/paths.md) - [Fixed Point Arithmetic](./safety/safemath.md) -- [Examples](./examples/README.md) - - [Permissioned Methods](./examples/permissioned.md) - - [Scheduling Runtime Tasks](./examples/loop.md) - - [Automated Token Issuance](./examples/token.md) - - [Social Network](./examples/social.md) -- [SRML Tour](./tour/README.md) - - [Treasury](./tour/treasury.md) +- [Runtime Configuration](./base/runtime.md) +- [Simple Treasury](./dao/README.md) +- [Token Curated Registry](./token/README.md) +- [NFT Ticket Machine](./nft/README.md) +- [Proof of Existence](./proof/README.md) +- [Social Network](./identity/README.md) - [Featured Tutorials](./base/dessert.md) ----------- diff --git a/src/advanced/random.md b/src/advanced/random.md index 803eabcd1..79a85752e 100644 --- a/src/advanced/random.md +++ b/src/advanced/random.md @@ -8,13 +8,14 @@ let random_seed = >::random_seed(); **To increase entropy**, we can introduce a nonce and a user-specified property. This provides us with a basic RNG on Substrate: ```rust +let random_seed = >::random_seed(); let nonce = ::get(); -let new_random = (>::random_seed(), nonce) +let new_random = (random_seed, nonce) .using_encoded(|b| Blake2Hasher::hash(b)) .using_encoded(|mut b| u64::decode(&mut b)) .expect("Hash must be bigger than 8 bytes; Qed"); ->::mutate(|n| *n += 1); ->::put(nonce + 1); +let new_nonce = ::get() + 1; +>::put(new_nonce); ``` **also see...** diff --git a/src/base/runtime.md b/src/base/runtime.md new file mode 100644 index 000000000..ff3ae264c --- /dev/null +++ b/src/base/runtime.md @@ -0,0 +1,5 @@ +# Runtime Configuration + +The runtime gives context to module development. Indeed, modules are libraries that expose runtime methods to adjust runtime storage. These libraries are configured in the runtime with explicit assignment of the types declared in the module [`Trait`](https://crates.parity.io/srml_example/trait.Trait.html). + +*To see how the [srml](https://github.com/paritytech/substrate/tree/master/srml) modules are configured, see the [`node/runtime`](https://github.com/paritytech/substrate/tree/master/node/runtime)* diff --git a/src/dao/README.md b/src/dao/README.md new file mode 100644 index 000000000..0ba15a0cf --- /dev/null +++ b/src/dao/README.md @@ -0,0 +1,7 @@ +# smpl-treasury + +In Polkadot, + +* [instantiating a pot]() +* [permissioned membership]() +* [scheduling funding]() \ No newline at end of file diff --git a/src/examples/loop.md b/src/dao/loop.md similarity index 100% rename from src/examples/loop.md rename to src/dao/loop.md diff --git a/src/examples/permissioned.md b/src/dao/permissioned.md similarity index 100% rename from src/examples/permissioned.md rename to src/dao/permissioned.md diff --git a/src/dao/treasury.md b/src/dao/treasury.md new file mode 100644 index 000000000..1dd4feb5c --- /dev/null +++ b/src/dao/treasury.md @@ -0,0 +1,95 @@ +# smpl-treasury + +This recipe implements a basic version of the treasury. +* add permissioned section + +This recipe demonstrates how [`srml-treasury`](https://github.com/paritytech/substrate/blob/master/srml/treasury/src/lib.rs) instantiates a pot of funds and schedules funding. *See [kitchen/treasury](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/treasury) for the full code* + +## Instantiate a Pot + +To instantiate a pool of funds, import [`ModuleId`](https://crates.parity.io/sr_primitives/struct.ModuleId.html) and [`AccountIdConversion`](https://crates.parity.io/sr_primitives/traits/trait.AccountIdConversion.html) from [`sr-primitives`](https://crates.parity.io/sr_primitives/index.html). + +```rust +use runtime_primitives::{ModuleId, traits::AccountIdConversion}; +``` + +With these imports, a `MODULE_ID` constant can be generated as an identifier for the pool of funds. This identifier can be converted into an `AccountId` with the `into_account()` method provided by the [`AccountIdConversion`](https://crates.parity.io/sr_primitives/traits/trait.AccountIdConversion.html) trait. + +```rust +const MODULE_ID: ModuleId = ModuleId(*b"example "); + +impl Module { + pub fn account_id() -> T::AccountId { + MODULE_ID.into_account() + } + + fn pot() -> BalanceOf { + T::Currency::free_balance(&Self::account_id()) + } +} +``` + +Accessing the pot's balance is as simple as using the [`Currency`](https://crates.parity.io/srml_support/traits/trait.Currency.html) trait to access the balance of the associated `AccountId`. + +## Proxy Transfers + +In [srml/treasury](https://github.com/paritytech/substrate/blob/master/srml/treasury/src/lib.rs), approved spending proposals are queued in runtime storage before they are scheduled for execution. For the example dispatch queue, each entry represents a request to transfer `BalanceOf` to `T::AccountId` from the pot. + +```rust +decl_storage! { + trait Store for Module as STreasury { + /// the amount, the address to which it is sent + SpendQ get(spend_q): Vec<(T::AccountId, BalanceOf)>; + } +} +``` + +In other words, the dispatch queue holds the `AccountId` of the recipient (destination) in the first field of the tuple and the `BalanceOf` in the second field. The runtime method for adding a spend request to the queue looks like this + +```rust +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + // uses the example treasury as a proxy for transferring funds + fn proxy_transfer(origin, dest: T::AccountId, amount: BalanceOf) -> Result { + let sender = ensure_signed(origin)?; + + let _ = T::Currency::transfer(&sender, &Self::account_id(), amount)?; + >::mutate(|requests| requests.push((dest.clone(), amount))); + Self::deposit_event(RawEvent::ProxyTransfer(dest, amount)); + Ok(()) + } + } +} +``` + +This method transfers some funds to the pot along with the request to transfer the same funds from the pot to a recipient (the input field `dest: T::AccountId`). + +NOTE: *Instead of relying on direct requests, [srml/treasury](https://github.com/paritytech/substrate/blob/master/srml/treasury/src/lib.rs) coordinates spending decisions through a proposal process.* + +## Scheduling Spending + +To schedule spending like [`srml/treasury`](https://github.com/paritytech/substrate/blob/master/srml/treasury/src/lib.rs), first add a configurable module constant in the `Trait`. This constant determines how often the spending queue is executed. + +```rust +pub trait Trait: system::Trait { + /// Period between successive spends. + type SpendPeriod: Get; +} +``` + +This constant is invoked in the runtime method [`on_finalize`](https://crates.parity.io/sr_primitives/traits/trait.OnFinalize.html) to schedule spending every `T::SpendPeriod::get()` blocks. + +```rust +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + // other runtime methods + fn on_finalize(n: T::BlockNumber) { + if (n % T::SpendPeriod::get()).is_zero() { + Self::spend_funds(); + } + } + } +} +``` + +*To see the logic within `spend_funds`, see the [kitchen/treasury](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/treasury).* This recipe could be extended to give priority to certain spend requests or set a cap on the spends for a given `spend_funds()` call. \ No newline at end of file diff --git a/src/examples/random.md b/src/examples/random.md deleted file mode 100644 index 9a6666bdc..000000000 --- a/src/examples/random.md +++ /dev/null @@ -1,25 +0,0 @@ -# Generating Randomness - -Substrate uses a safe mixing algorithm to generate randomness using the entropy of previous blocks. Because it is dependent on previous blocks, it can take many blocks for the seed to change. - -```rust -let random_seed = >::random_seed(); -``` - -**To increase entropy**, we can introduce a nonce and a user-specified property. This provides us with a basic RNG on Substrate: -```rust -let nonce = ::get(); -let new_random = (>::random_seed(), nonce) - .using_encoded(|b| Blake2Hasher::hash(b)) - .using_encoded(|mut b| u64::decode(&mut b)) - .expect("Hash must be bigger than 8 bytes; Qed"); - ->::mutate(|n| *n += 1); -``` - - -**also see...** - -* https://github.com/paritytech/ink/issues/57 - -**[Back to Recipes](https://substrate.dev/recipes/)** \ No newline at end of file diff --git a/src/identity/README.md b/src/identity/README.md new file mode 100644 index 000000000..93e34f838 --- /dev/null +++ b/src/identity/README.md @@ -0,0 +1,3 @@ +# Social Network + +* did `=>` social network using hyperledger grid code \ No newline at end of file diff --git a/src/nft/README.md b/src/nft/README.md new file mode 100644 index 000000000..b68454848 --- /dev/null +++ b/src/nft/README.md @@ -0,0 +1,3 @@ +# NFT Ticket Machine + +* kitties `=>` collectables `=>` ticketing \ No newline at end of file diff --git a/src/proof/README.md b/src/proof/README.md new file mode 100644 index 000000000..c11705490 --- /dev/null +++ b/src/proof/README.md @@ -0,0 +1,3 @@ +# Proof of Existence + +poe tutorial `=>` {ofc_workers, proof-based APIs} \ No newline at end of file diff --git a/src/token/README.md b/src/token/README.md new file mode 100644 index 000000000..9feaebece --- /dev/null +++ b/src/token/README.md @@ -0,0 +1,8 @@ +# Token Curated Registry + +* [token](./token.md) +* [tcr](./tcr.md) + +* reference the feeless token tutorial + +* add bonding curve tutorial as an extension \ No newline at end of file diff --git a/src/token/tcr.md b/src/token/tcr.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/examples/token.md b/src/token/token.md similarity index 100% rename from src/examples/token.md rename to src/token/token.md From 1a6162dd5fcc028806d2abd416ff92a6337e1b4b Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Fri, 20 Sep 2019 01:42:19 +0200 Subject: [PATCH 05/23] restructure kitchen --- kitchen/README.md | 3 + kitchen/{ => modules}/adder/Cargo.toml | 0 kitchen/{ => modules}/adder/src/lib.rs | 0 kitchen/{ => modules}/constants/Cargo.toml | 0 kitchen/{ => modules}/constants/src/lib.rs | 0 kitchen/{ => modules}/imbalances/Cargo.toml | 0 kitchen/{ => modules}/imbalances/src/lib.rs | 0 kitchen/{ => modules}/list/Cargo.toml | 0 kitchen/{ => modules}/list/src/lib.rs | 0 kitchen/{ => modules}/lockable/Cargo.toml | 0 kitchen/{ => modules}/lockable/src/lib.rs | 0 kitchen/{ => modules}/loop/Cargo.toml | 0 kitchen/{ => modules}/loop/src/lib.rs | 0 kitchen/{ => modules}/nstructs/Cargo.toml | 0 kitchen/{ => modules}/nstructs/src/lib.rs | 0 kitchen/{ => modules}/permissioned/Cargo.toml | 0 kitchen/{ => modules}/permissioned/src/lib.rs | 0 kitchen/{ => modules}/random/Cargo.toml | 0 kitchen/{ => modules}/random/README.md | 0 kitchen/{ => modules}/random/src/lib.rs | 0 kitchen/{ => modules}/reservable/Cargo.toml | 0 kitchen/{ => modules}/reservable/README.md | 0 kitchen/{ => modules}/reservable/src/lib.rs | 0 kitchen/{ => modules}/social/Cargo.toml | 0 kitchen/{ => modules}/social/src/lib.rs | 0 kitchen/{ => modules}/token/Cargo.toml | 0 kitchen/{ => modules}/token/src/lib.rs | 0 kitchen/{ => modules}/treasury/Cargo.toml | 0 kitchen/{ => modules}/treasury/README.md | 0 kitchen/{ => modules}/treasury/src/lib.rs | 0 kitchen/{ => modules}/value/Cargo.toml | 0 kitchen/{ => modules}/value/src/lib.rs | 0 .../substrate-node-template/.gitignore | 0 .../substrate-node-template/Cargo.toml | 0 .../substrate-node-template/LICENSE | 0 .../substrate-node-template/README.md | 0 .../substrate-node-template/build.rs | 0 .../runtime/Cargo.toml | 0 .../runtime/src/lib.rs | 0 .../runtime/wasm/Cargo.toml | 0 .../runtime/wasm/build.sh | 0 .../runtime/wasm/src/lib.rs | 0 .../substrate-node-template/scripts/build.sh | 0 .../substrate-node-template/scripts/init.sh | 0 .../substrate-node-template/src/chain_spec.rs | 0 .../substrate-node-template/src/cli.rs | 0 .../substrate-node-template/src/error.rs | 0 .../substrate-node-template/src/main.rs | 0 .../substrate-node-template/src/service.rs | 0 src/dao/permissioned.md | 103 ------------------ src/dao/treasury.md | 1 + src/examples/README.md | 6 - src/examples/recipes_readme.md | 6 - src/{examples => identity}/social.md | 0 54 files changed, 4 insertions(+), 115 deletions(-) rename kitchen/{ => modules}/adder/Cargo.toml (100%) rename kitchen/{ => modules}/adder/src/lib.rs (100%) rename kitchen/{ => modules}/constants/Cargo.toml (100%) rename kitchen/{ => modules}/constants/src/lib.rs (100%) rename kitchen/{ => modules}/imbalances/Cargo.toml (100%) rename kitchen/{ => modules}/imbalances/src/lib.rs (100%) rename kitchen/{ => modules}/list/Cargo.toml (100%) rename kitchen/{ => modules}/list/src/lib.rs (100%) rename kitchen/{ => modules}/lockable/Cargo.toml (100%) rename kitchen/{ => modules}/lockable/src/lib.rs (100%) rename kitchen/{ => modules}/loop/Cargo.toml (100%) rename kitchen/{ => modules}/loop/src/lib.rs (100%) rename kitchen/{ => modules}/nstructs/Cargo.toml (100%) rename kitchen/{ => modules}/nstructs/src/lib.rs (100%) rename kitchen/{ => modules}/permissioned/Cargo.toml (100%) rename kitchen/{ => modules}/permissioned/src/lib.rs (100%) rename kitchen/{ => modules}/random/Cargo.toml (100%) rename kitchen/{ => modules}/random/README.md (100%) rename kitchen/{ => modules}/random/src/lib.rs (100%) rename kitchen/{ => modules}/reservable/Cargo.toml (100%) rename kitchen/{ => modules}/reservable/README.md (100%) rename kitchen/{ => modules}/reservable/src/lib.rs (100%) rename kitchen/{ => modules}/social/Cargo.toml (100%) rename kitchen/{ => modules}/social/src/lib.rs (100%) rename kitchen/{ => modules}/token/Cargo.toml (100%) rename kitchen/{ => modules}/token/src/lib.rs (100%) rename kitchen/{ => modules}/treasury/Cargo.toml (100%) rename kitchen/{ => modules}/treasury/README.md (100%) rename kitchen/{ => modules}/treasury/src/lib.rs (100%) rename kitchen/{ => modules}/value/Cargo.toml (100%) rename kitchen/{ => modules}/value/src/lib.rs (100%) rename kitchen/{ => runtimes}/substrate-node-template/.gitignore (100%) rename kitchen/{ => runtimes}/substrate-node-template/Cargo.toml (100%) rename kitchen/{ => runtimes}/substrate-node-template/LICENSE (100%) rename kitchen/{ => runtimes}/substrate-node-template/README.md (100%) rename kitchen/{ => runtimes}/substrate-node-template/build.rs (100%) rename kitchen/{ => runtimes}/substrate-node-template/runtime/Cargo.toml (100%) rename kitchen/{ => runtimes}/substrate-node-template/runtime/src/lib.rs (100%) rename kitchen/{ => runtimes}/substrate-node-template/runtime/wasm/Cargo.toml (100%) rename kitchen/{ => runtimes}/substrate-node-template/runtime/wasm/build.sh (100%) rename kitchen/{ => runtimes}/substrate-node-template/runtime/wasm/src/lib.rs (100%) rename kitchen/{ => runtimes}/substrate-node-template/scripts/build.sh (100%) rename kitchen/{ => runtimes}/substrate-node-template/scripts/init.sh (100%) rename kitchen/{ => runtimes}/substrate-node-template/src/chain_spec.rs (100%) rename kitchen/{ => runtimes}/substrate-node-template/src/cli.rs (100%) rename kitchen/{ => runtimes}/substrate-node-template/src/error.rs (100%) rename kitchen/{ => runtimes}/substrate-node-template/src/main.rs (100%) rename kitchen/{ => runtimes}/substrate-node-template/src/service.rs (100%) delete mode 100644 src/examples/README.md delete mode 100644 src/examples/recipes_readme.md rename src/{examples => identity}/social.md (100%) diff --git a/kitchen/README.md b/kitchen/README.md index 2d5bc6d00..959188924 100644 --- a/kitchen/README.md +++ b/kitchen/README.md @@ -2,6 +2,9 @@ The kitchen is for *cooking* recipes. It is structured like the main recipes build as specified in [src/SUMMARY.md](../src/SUMMARY.md), except every code sample is stored as a library via the [substrate-module-template](https://github.com/shawntabrizi/substrate-module-template). +**NEW STRUCTURE** +* divided up into `modules` and `runtimes`... + **Event**: effectively logging, scheduling, and reacting to events defined in the `decl_event` block of the runtime. * [Adding Machine](./adder/) diff --git a/kitchen/adder/Cargo.toml b/kitchen/modules/adder/Cargo.toml similarity index 100% rename from kitchen/adder/Cargo.toml rename to kitchen/modules/adder/Cargo.toml diff --git a/kitchen/adder/src/lib.rs b/kitchen/modules/adder/src/lib.rs similarity index 100% rename from kitchen/adder/src/lib.rs rename to kitchen/modules/adder/src/lib.rs diff --git a/kitchen/constants/Cargo.toml b/kitchen/modules/constants/Cargo.toml similarity index 100% rename from kitchen/constants/Cargo.toml rename to kitchen/modules/constants/Cargo.toml diff --git a/kitchen/constants/src/lib.rs b/kitchen/modules/constants/src/lib.rs similarity index 100% rename from kitchen/constants/src/lib.rs rename to kitchen/modules/constants/src/lib.rs diff --git a/kitchen/imbalances/Cargo.toml b/kitchen/modules/imbalances/Cargo.toml similarity index 100% rename from kitchen/imbalances/Cargo.toml rename to kitchen/modules/imbalances/Cargo.toml diff --git a/kitchen/imbalances/src/lib.rs b/kitchen/modules/imbalances/src/lib.rs similarity index 100% rename from kitchen/imbalances/src/lib.rs rename to kitchen/modules/imbalances/src/lib.rs diff --git a/kitchen/list/Cargo.toml b/kitchen/modules/list/Cargo.toml similarity index 100% rename from kitchen/list/Cargo.toml rename to kitchen/modules/list/Cargo.toml diff --git a/kitchen/list/src/lib.rs b/kitchen/modules/list/src/lib.rs similarity index 100% rename from kitchen/list/src/lib.rs rename to kitchen/modules/list/src/lib.rs diff --git a/kitchen/lockable/Cargo.toml b/kitchen/modules/lockable/Cargo.toml similarity index 100% rename from kitchen/lockable/Cargo.toml rename to kitchen/modules/lockable/Cargo.toml diff --git a/kitchen/lockable/src/lib.rs b/kitchen/modules/lockable/src/lib.rs similarity index 100% rename from kitchen/lockable/src/lib.rs rename to kitchen/modules/lockable/src/lib.rs diff --git a/kitchen/loop/Cargo.toml b/kitchen/modules/loop/Cargo.toml similarity index 100% rename from kitchen/loop/Cargo.toml rename to kitchen/modules/loop/Cargo.toml diff --git a/kitchen/loop/src/lib.rs b/kitchen/modules/loop/src/lib.rs similarity index 100% rename from kitchen/loop/src/lib.rs rename to kitchen/modules/loop/src/lib.rs diff --git a/kitchen/nstructs/Cargo.toml b/kitchen/modules/nstructs/Cargo.toml similarity index 100% rename from kitchen/nstructs/Cargo.toml rename to kitchen/modules/nstructs/Cargo.toml diff --git a/kitchen/nstructs/src/lib.rs b/kitchen/modules/nstructs/src/lib.rs similarity index 100% rename from kitchen/nstructs/src/lib.rs rename to kitchen/modules/nstructs/src/lib.rs diff --git a/kitchen/permissioned/Cargo.toml b/kitchen/modules/permissioned/Cargo.toml similarity index 100% rename from kitchen/permissioned/Cargo.toml rename to kitchen/modules/permissioned/Cargo.toml diff --git a/kitchen/permissioned/src/lib.rs b/kitchen/modules/permissioned/src/lib.rs similarity index 100% rename from kitchen/permissioned/src/lib.rs rename to kitchen/modules/permissioned/src/lib.rs diff --git a/kitchen/random/Cargo.toml b/kitchen/modules/random/Cargo.toml similarity index 100% rename from kitchen/random/Cargo.toml rename to kitchen/modules/random/Cargo.toml diff --git a/kitchen/random/README.md b/kitchen/modules/random/README.md similarity index 100% rename from kitchen/random/README.md rename to kitchen/modules/random/README.md diff --git a/kitchen/random/src/lib.rs b/kitchen/modules/random/src/lib.rs similarity index 100% rename from kitchen/random/src/lib.rs rename to kitchen/modules/random/src/lib.rs diff --git a/kitchen/reservable/Cargo.toml b/kitchen/modules/reservable/Cargo.toml similarity index 100% rename from kitchen/reservable/Cargo.toml rename to kitchen/modules/reservable/Cargo.toml diff --git a/kitchen/reservable/README.md b/kitchen/modules/reservable/README.md similarity index 100% rename from kitchen/reservable/README.md rename to kitchen/modules/reservable/README.md diff --git a/kitchen/reservable/src/lib.rs b/kitchen/modules/reservable/src/lib.rs similarity index 100% rename from kitchen/reservable/src/lib.rs rename to kitchen/modules/reservable/src/lib.rs diff --git a/kitchen/social/Cargo.toml b/kitchen/modules/social/Cargo.toml similarity index 100% rename from kitchen/social/Cargo.toml rename to kitchen/modules/social/Cargo.toml diff --git a/kitchen/social/src/lib.rs b/kitchen/modules/social/src/lib.rs similarity index 100% rename from kitchen/social/src/lib.rs rename to kitchen/modules/social/src/lib.rs diff --git a/kitchen/token/Cargo.toml b/kitchen/modules/token/Cargo.toml similarity index 100% rename from kitchen/token/Cargo.toml rename to kitchen/modules/token/Cargo.toml diff --git a/kitchen/token/src/lib.rs b/kitchen/modules/token/src/lib.rs similarity index 100% rename from kitchen/token/src/lib.rs rename to kitchen/modules/token/src/lib.rs diff --git a/kitchen/treasury/Cargo.toml b/kitchen/modules/treasury/Cargo.toml similarity index 100% rename from kitchen/treasury/Cargo.toml rename to kitchen/modules/treasury/Cargo.toml diff --git a/kitchen/treasury/README.md b/kitchen/modules/treasury/README.md similarity index 100% rename from kitchen/treasury/README.md rename to kitchen/modules/treasury/README.md diff --git a/kitchen/treasury/src/lib.rs b/kitchen/modules/treasury/src/lib.rs similarity index 100% rename from kitchen/treasury/src/lib.rs rename to kitchen/modules/treasury/src/lib.rs diff --git a/kitchen/value/Cargo.toml b/kitchen/modules/value/Cargo.toml similarity index 100% rename from kitchen/value/Cargo.toml rename to kitchen/modules/value/Cargo.toml diff --git a/kitchen/value/src/lib.rs b/kitchen/modules/value/src/lib.rs similarity index 100% rename from kitchen/value/src/lib.rs rename to kitchen/modules/value/src/lib.rs diff --git a/kitchen/substrate-node-template/.gitignore b/kitchen/runtimes/substrate-node-template/.gitignore similarity index 100% rename from kitchen/substrate-node-template/.gitignore rename to kitchen/runtimes/substrate-node-template/.gitignore diff --git a/kitchen/substrate-node-template/Cargo.toml b/kitchen/runtimes/substrate-node-template/Cargo.toml similarity index 100% rename from kitchen/substrate-node-template/Cargo.toml rename to kitchen/runtimes/substrate-node-template/Cargo.toml diff --git a/kitchen/substrate-node-template/LICENSE b/kitchen/runtimes/substrate-node-template/LICENSE similarity index 100% rename from kitchen/substrate-node-template/LICENSE rename to kitchen/runtimes/substrate-node-template/LICENSE diff --git a/kitchen/substrate-node-template/README.md b/kitchen/runtimes/substrate-node-template/README.md similarity index 100% rename from kitchen/substrate-node-template/README.md rename to kitchen/runtimes/substrate-node-template/README.md diff --git a/kitchen/substrate-node-template/build.rs b/kitchen/runtimes/substrate-node-template/build.rs similarity index 100% rename from kitchen/substrate-node-template/build.rs rename to kitchen/runtimes/substrate-node-template/build.rs diff --git a/kitchen/substrate-node-template/runtime/Cargo.toml b/kitchen/runtimes/substrate-node-template/runtime/Cargo.toml similarity index 100% rename from kitchen/substrate-node-template/runtime/Cargo.toml rename to kitchen/runtimes/substrate-node-template/runtime/Cargo.toml diff --git a/kitchen/substrate-node-template/runtime/src/lib.rs b/kitchen/runtimes/substrate-node-template/runtime/src/lib.rs similarity index 100% rename from kitchen/substrate-node-template/runtime/src/lib.rs rename to kitchen/runtimes/substrate-node-template/runtime/src/lib.rs diff --git a/kitchen/substrate-node-template/runtime/wasm/Cargo.toml b/kitchen/runtimes/substrate-node-template/runtime/wasm/Cargo.toml similarity index 100% rename from kitchen/substrate-node-template/runtime/wasm/Cargo.toml rename to kitchen/runtimes/substrate-node-template/runtime/wasm/Cargo.toml diff --git a/kitchen/substrate-node-template/runtime/wasm/build.sh b/kitchen/runtimes/substrate-node-template/runtime/wasm/build.sh similarity index 100% rename from kitchen/substrate-node-template/runtime/wasm/build.sh rename to kitchen/runtimes/substrate-node-template/runtime/wasm/build.sh diff --git a/kitchen/substrate-node-template/runtime/wasm/src/lib.rs b/kitchen/runtimes/substrate-node-template/runtime/wasm/src/lib.rs similarity index 100% rename from kitchen/substrate-node-template/runtime/wasm/src/lib.rs rename to kitchen/runtimes/substrate-node-template/runtime/wasm/src/lib.rs diff --git a/kitchen/substrate-node-template/scripts/build.sh b/kitchen/runtimes/substrate-node-template/scripts/build.sh similarity index 100% rename from kitchen/substrate-node-template/scripts/build.sh rename to kitchen/runtimes/substrate-node-template/scripts/build.sh diff --git a/kitchen/substrate-node-template/scripts/init.sh b/kitchen/runtimes/substrate-node-template/scripts/init.sh similarity index 100% rename from kitchen/substrate-node-template/scripts/init.sh rename to kitchen/runtimes/substrate-node-template/scripts/init.sh diff --git a/kitchen/substrate-node-template/src/chain_spec.rs b/kitchen/runtimes/substrate-node-template/src/chain_spec.rs similarity index 100% rename from kitchen/substrate-node-template/src/chain_spec.rs rename to kitchen/runtimes/substrate-node-template/src/chain_spec.rs diff --git a/kitchen/substrate-node-template/src/cli.rs b/kitchen/runtimes/substrate-node-template/src/cli.rs similarity index 100% rename from kitchen/substrate-node-template/src/cli.rs rename to kitchen/runtimes/substrate-node-template/src/cli.rs diff --git a/kitchen/substrate-node-template/src/error.rs b/kitchen/runtimes/substrate-node-template/src/error.rs similarity index 100% rename from kitchen/substrate-node-template/src/error.rs rename to kitchen/runtimes/substrate-node-template/src/error.rs diff --git a/kitchen/substrate-node-template/src/main.rs b/kitchen/runtimes/substrate-node-template/src/main.rs similarity index 100% rename from kitchen/substrate-node-template/src/main.rs rename to kitchen/runtimes/substrate-node-template/src/main.rs diff --git a/kitchen/substrate-node-template/src/service.rs b/kitchen/runtimes/substrate-node-template/src/service.rs similarity index 100% rename from kitchen/substrate-node-template/src/service.rs rename to kitchen/runtimes/substrate-node-template/src/service.rs diff --git a/src/dao/permissioned.md b/src/dao/permissioned.md index 77465f580..e69de29bb 100644 --- a/src/dao/permissioned.md +++ b/src/dao/permissioned.md @@ -1,103 +0,0 @@ -# Permissioned Methods - -## Single Owner Access Control - -This recipe contains a permissioned function which can only be called by the *Owner*. An event is emitted when the function is successfully executed. - -The imports are similar to previous event recipes with the additional import of the `support::StorageValue`. -```rust -// other imports -use support::{StorageValue}; -``` - -In the [`decl_storage`](https://crates.parity.io/srml_support_procedural/macro.decl_storage.html) block, designate the `AccountId` of the owner that can invoke the permissioned function. - -```rust -decl_storage! { - trait Store for Module as RuntimeExampleStorage { - Owner get(owner): T::AccountId; - } -} -``` - -When this `AccountId` is changed, it is useful to emit an event to notify any relevant actors off-chain. - -```rust -decl_event!( - pub enum Event where AccountId = ::AccountId { - OwnershipTransferred(AccountId, AccountId), - } -); -``` - -The main logic is contained in the runtime methods. Our first runtime method initiates the ownership. Before doing so, it verifies that no current owner exists. - -```rust -/// in decl_module -fn init_ownership(origin) -> Result { - ensure!(!>::exists(), "Owner already exists"); - let sender = ensure_signed(origin)?; - >::put(&sender); - Self::deposit_event(RawEvent::OwnershipTransferred(sender.clone(), sender)); - Ok(()) -} -``` - - -The second runtime method transfers ownership. Before doing so, it checks that the invocation is made by the current owner. -```rust -fn transfer_ownership(origin, newOwner: T::AccountId) -> Result { - let sender = ensure_signed(origin)?; - ensure!(sender == Self::owner(), "This function can only be called by the owner"); - >::put(&newOwner); - Self::deposit_event(RawEvent::OwnershipTransferred(sender, newOwner)); - Ok(()) -} -``` - -## Group Membership Authentication - -This recipe is extended to define permissioned functions which limit invocations to members of a group. The group's membership is managed in runtime storage: - -```rust -// decl_storage block -Members get(members): Vec; -``` - -Runtime methods `add_member` demonstrates how members can be added. In other projects, existing members might vote on new member applications instead of automatic admission. - -```rust -fn add_member(origin) -> Result { - let new_member = ensure_signed(origin)?; - ensure!(!Self::is_member(&new_member), "already a member"); - - >::mutate(|mem| mem.push(new_member.clone())); // change to append after 3071 merged - Self::deposit_event(RawEvent::AddMember(new_member)); - Ok(()) -} -``` - -The `remove_member` method is similar. The only difference is that we are removing the member rather than pushing a new member to the method. - -```rust -fn remove_member(origin) -> Result { - let old_member = ensure_signed(origin)?; - - ensure!(Self::is_member(&old_member), "not a member"); - // keep all members except for the member in question - >::mutate(|mem| mem.retain(|m| m != &old_member)); - Self::deposit_event(RawEvent::RemoveMember(old_member)); - Ok(()) -} -``` - -The `ensure` checks are symmetric in the sense that `add_member` requires that the member in question is not already a member, while the `remove_member` method requires membership. To check membership within the runtime, we define the helper `is_member` method: - -```rust -// impl Module block -pub fn is_member(who: &T::AccountId) -> bool { - Self::members().contains(who) -} -``` - -This example can easily be extended to define criteria for adding and removing members. A well-written example can be found in [`srml/collective`](https://github.com/paritytech/substrate/blob/master/srml/collective/src/lib.rs), which also uses a `Vec` to manage membership. diff --git a/src/dao/treasury.md b/src/dao/treasury.md index 1dd4feb5c..24bb2ae2a 100644 --- a/src/dao/treasury.md +++ b/src/dao/treasury.md @@ -2,6 +2,7 @@ This recipe implements a basic version of the treasury. * add permissioned section +* This recipe demonstrates how [`srml-treasury`](https://github.com/paritytech/substrate/blob/master/srml/treasury/src/lib.rs) instantiates a pot of funds and schedules funding. *See [kitchen/treasury](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/treasury) for the full code* diff --git a/src/examples/README.md b/src/examples/README.md deleted file mode 100644 index 4de259272..000000000 --- a/src/examples/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Design Patterns - -* [Permissioned Methods](./permissioned.md) -* [Blockchain Event Loop](./loop.md) - -*in progress*: [generating randomness](https://github.com/substrate-developer-hub/recipes/blob/master/src/common/random.md) \ No newline at end of file diff --git a/src/examples/recipes_readme.md b/src/examples/recipes_readme.md deleted file mode 100644 index 9c15c74b6..000000000 --- a/src/examples/recipes_readme.md +++ /dev/null @@ -1,6 +0,0 @@ -# Recipes - -* [token transfer](./token.md) -* [social network](./social.md) - -*see more guided tutorials and samples on the [Substrate Developer Hub](https://github.com/substrate-developer-hub)* \ No newline at end of file diff --git a/src/examples/social.md b/src/identity/social.md similarity index 100% rename from src/examples/social.md rename to src/identity/social.md From a19c2d261f338675dcab9cb4a6387f4b84d3d0df Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 23 Sep 2019 11:18:43 +0200 Subject: [PATCH 06/23] add intro to rust recipe --- .../.gitignore | 0 .../Cargo.toml | 0 .../LICENSE | 0 .../README.md | 4 +- .../build.rs | 0 .../runtime/Cargo.toml | 0 .../runtime/src/lib.rs | 0 .../runtime/wasm/Cargo.toml | 0 .../runtime/wasm/build.sh | 0 .../runtime/wasm/src/lib.rs | 0 .../scripts/build.sh | 0 .../scripts/init.sh | 0 .../src/chain_spec.rs | 0 .../src/cli.rs | 0 .../src/error.rs | 0 .../src/main.rs | 0 .../src/service.rs | 0 src/SUMMARY.md | 7 +-- src/base/rust.md | 50 ++++++++++++++++++- src/basics/helloworld.md | 3 ++ src/safety/econ_cost.md | 11 ++++ src/safety/optimizations.md | 2 +- 22 files changed, 69 insertions(+), 8 deletions(-) rename kitchen/runtimes/{substrate-node-template => supernode}/.gitignore (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/Cargo.toml (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/LICENSE (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/README.md (96%) rename kitchen/runtimes/{substrate-node-template => supernode}/build.rs (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/runtime/Cargo.toml (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/runtime/src/lib.rs (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/runtime/wasm/Cargo.toml (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/runtime/wasm/build.sh (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/runtime/wasm/src/lib.rs (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/scripts/build.sh (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/scripts/init.sh (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/src/chain_spec.rs (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/src/cli.rs (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/src/error.rs (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/src/main.rs (100%) rename kitchen/runtimes/{substrate-node-template => supernode}/src/service.rs (100%) create mode 100644 src/basics/helloworld.md create mode 100644 src/safety/econ_cost.md diff --git a/kitchen/runtimes/substrate-node-template/.gitignore b/kitchen/runtimes/supernode/.gitignore similarity index 100% rename from kitchen/runtimes/substrate-node-template/.gitignore rename to kitchen/runtimes/supernode/.gitignore diff --git a/kitchen/runtimes/substrate-node-template/Cargo.toml b/kitchen/runtimes/supernode/Cargo.toml similarity index 100% rename from kitchen/runtimes/substrate-node-template/Cargo.toml rename to kitchen/runtimes/supernode/Cargo.toml diff --git a/kitchen/runtimes/substrate-node-template/LICENSE b/kitchen/runtimes/supernode/LICENSE similarity index 100% rename from kitchen/runtimes/substrate-node-template/LICENSE rename to kitchen/runtimes/supernode/LICENSE diff --git a/kitchen/runtimes/substrate-node-template/README.md b/kitchen/runtimes/supernode/README.md similarity index 96% rename from kitchen/runtimes/substrate-node-template/README.md rename to kitchen/runtimes/supernode/README.md index 4d616be7f..2989d6e37 100644 --- a/kitchen/runtimes/substrate-node-template/README.md +++ b/kitchen/runtimes/supernode/README.md @@ -1,6 +1,6 @@ -# Template Node +# Super Node -A new SRML-based Substrate node, ready for hacking. +A node with all the module configurations from [modules](./modules) # Building diff --git a/kitchen/runtimes/substrate-node-template/build.rs b/kitchen/runtimes/supernode/build.rs similarity index 100% rename from kitchen/runtimes/substrate-node-template/build.rs rename to kitchen/runtimes/supernode/build.rs diff --git a/kitchen/runtimes/substrate-node-template/runtime/Cargo.toml b/kitchen/runtimes/supernode/runtime/Cargo.toml similarity index 100% rename from kitchen/runtimes/substrate-node-template/runtime/Cargo.toml rename to kitchen/runtimes/supernode/runtime/Cargo.toml diff --git a/kitchen/runtimes/substrate-node-template/runtime/src/lib.rs b/kitchen/runtimes/supernode/runtime/src/lib.rs similarity index 100% rename from kitchen/runtimes/substrate-node-template/runtime/src/lib.rs rename to kitchen/runtimes/supernode/runtime/src/lib.rs diff --git a/kitchen/runtimes/substrate-node-template/runtime/wasm/Cargo.toml b/kitchen/runtimes/supernode/runtime/wasm/Cargo.toml similarity index 100% rename from kitchen/runtimes/substrate-node-template/runtime/wasm/Cargo.toml rename to kitchen/runtimes/supernode/runtime/wasm/Cargo.toml diff --git a/kitchen/runtimes/substrate-node-template/runtime/wasm/build.sh b/kitchen/runtimes/supernode/runtime/wasm/build.sh similarity index 100% rename from kitchen/runtimes/substrate-node-template/runtime/wasm/build.sh rename to kitchen/runtimes/supernode/runtime/wasm/build.sh diff --git a/kitchen/runtimes/substrate-node-template/runtime/wasm/src/lib.rs b/kitchen/runtimes/supernode/runtime/wasm/src/lib.rs similarity index 100% rename from kitchen/runtimes/substrate-node-template/runtime/wasm/src/lib.rs rename to kitchen/runtimes/supernode/runtime/wasm/src/lib.rs diff --git a/kitchen/runtimes/substrate-node-template/scripts/build.sh b/kitchen/runtimes/supernode/scripts/build.sh similarity index 100% rename from kitchen/runtimes/substrate-node-template/scripts/build.sh rename to kitchen/runtimes/supernode/scripts/build.sh diff --git a/kitchen/runtimes/substrate-node-template/scripts/init.sh b/kitchen/runtimes/supernode/scripts/init.sh similarity index 100% rename from kitchen/runtimes/substrate-node-template/scripts/init.sh rename to kitchen/runtimes/supernode/scripts/init.sh diff --git a/kitchen/runtimes/substrate-node-template/src/chain_spec.rs b/kitchen/runtimes/supernode/src/chain_spec.rs similarity index 100% rename from kitchen/runtimes/substrate-node-template/src/chain_spec.rs rename to kitchen/runtimes/supernode/src/chain_spec.rs diff --git a/kitchen/runtimes/substrate-node-template/src/cli.rs b/kitchen/runtimes/supernode/src/cli.rs similarity index 100% rename from kitchen/runtimes/substrate-node-template/src/cli.rs rename to kitchen/runtimes/supernode/src/cli.rs diff --git a/kitchen/runtimes/substrate-node-template/src/error.rs b/kitchen/runtimes/supernode/src/error.rs similarity index 100% rename from kitchen/runtimes/substrate-node-template/src/error.rs rename to kitchen/runtimes/supernode/src/error.rs diff --git a/kitchen/runtimes/substrate-node-template/src/main.rs b/kitchen/runtimes/supernode/src/main.rs similarity index 100% rename from kitchen/runtimes/substrate-node-template/src/main.rs rename to kitchen/runtimes/supernode/src/main.rs diff --git a/kitchen/runtimes/substrate-node-template/src/service.rs b/kitchen/runtimes/supernode/src/service.rs similarity index 100% rename from kitchen/runtimes/substrate-node-template/src/service.rs rename to kitchen/runtimes/supernode/src/service.rs diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 599e2697b..e5bd374ea 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -2,14 +2,15 @@ - [Introduction](./base/README.md) - [Set Up Substrate](./base/setup.md) -- [Rust Review](./base/rust.md) +- [Learn Rust](./base/rust.md) +- [Hello World](./basics/hello.md) - [Module Fundamentals](./basics/README.md) - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - [Maps](./basics/list.md) - [Configurable Constants](./basics/constants.md) -- [Types and Traits](./types/README.md) - - [Currency Type Usage](./types/collateral.md) +- [Traits](./types/README.md) + - [Currency Types](./types/collateral.md) - [Safety First](./safety/README.md) - [Declarative Programming](./safety/paths.md) - [Fixed Point Arithmetic](./safety/safemath.md) diff --git a/src/base/rust.md b/src/base/rust.md index 4d77bd35a..e71f7de13 100644 --- a/src/base/rust.md +++ b/src/base/rust.md @@ -1,3 +1,49 @@ -# Intro to Rust +# Learn Rust -* TODO \ No newline at end of file +To be productive with [substrate](https://github.com/substrate), it is necessary to become proficient in Rust. Fortunately, the Rust community is known for comprehensive documentation and tutorials. The canonical resource for learning Rust is [The Rust Book](https://doc.rust-lang.org/book/index.html). [Rust by Example](https://doc.rust-lang.org/rust-by-example/index.html) is also a good reference for learning how to use popular crates within the Rust ecosystem. + +To become more familiar with commmon design patterns in Rust, see the following: +* [Official Rust API Guidelines](https://rust-lang-nursery.github.io/api-guidelines/about.html) +* [Rust Unofficial Design Patterns](https://github.com/rust-unofficial/patterns) +* [Elegant Library API Guidelines](https://deterministic.space/elegant-apis-in-rust.html) by Pascal Hertleif + +To optimize runtime performance, Substrate developers should make use of iterators, traits, and Rust's other "*zero cost* abstractions": +* [Abstraction without overhead: traits in Rust](https://blog.rust-lang.org/2015/05/11/traits.html), [related conferencce talk](https://www.youtube.com/watch?v=Sn3JklPAVLk) +* [Effectively Using Iterators in Rust](https://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html), *[iterate over a slice rather than a `vec!`](https://twitter.com/heinz_gies/status/1121490424739303425)* +* [Achieving Warp Speed with Rust](http://troubles.md/posts/rust-optimization/) + +It is not (immediately) necessary to become familiar with multithreading because the runtime operates in a [single-threaded context](https://www.tutorialspoint.com/single-threaded-and-multi-threaded-processes). Even so, an optimized [substrate node](https://github.com/paritytech/substrate/tree/master/node) architecture will use a custom RPC interface. Moreover, the runtime might take advantage of the [offchain workers API](https://substrate.dev/docs/en/next/overview/off-chain-workers) to minimize the computation executed on-chain. Effectively using these features requires increased familiarity with advanced Rust. + +For a high-level overview of concurrency in Rust, Stjepan Glavina provides the following descriptions in [Lock-free Rust: Crossbeam in 2019](https://stjepang.github.io/2019/01/29/lock-free-rust-crossbeam-in-2019.html): +* **[Rayon](https://github.com/rayon-rs/rayon)** splits your data into distinct pieces, gives each piece to a thread to do some kind of computation on it, and finally aggregates results. Its goal is to distribute CPU-intensive tasks onto a thread pool. +* **[Tokio](https://github.com/tokio-rs/tokio)** runs tasks which sometimes need to be paused in order to wait for asynchronous events. Handling tons of such tasks is no problem. Its goal is to distribute IO-intensive tasks onto a thread pool. +* **[Crossbeam](https://github.com/crossbeam-rs/crossbeam)** is all about low-level concurrency: atomics, concurrent data structures, synchronization primitives. Same idea as the `std::sync` module, but bigger. Its goal is to provide tools on top of which libraries like Rayon and Tokio can be built. + +To dive deeper down these rabbit holes: +* [Asynchrony](#async) +* [Concurrency](#concurrency) + +### Asynchrony +[Are we `async` yet?](https://areweasyncyet.rs/) + +**Conceptual** +* [Introduction to Async/Await Programming (withoutboats/wakers-i)](https://boats.gitlab.io/blog/post/wakers-i/) +* [Futures (by Aaron Turon)](http://aturon.github.io/2016/08/11/futures/) +* [RustLatam 2019 - Without Boats: Zero-Cost Async IO](https://www.youtube.com/watch?v=skos4B5x7qE) + +**Projects** +* [Rust Asynchronous Ecosystem Working Group](https://github.com/rustasync) +* [romio](https://github.com/withoutboats/romio) +* [Tokio Docs](https://tokio.rs/docs/overview/) + +### Concurrency + +**Conceptual** +* [Rust Concurrency Explained](https://www.youtube.com/watch?v=Dbytx0ivH7Q) +* [Lock-free Rust: Crossbeam in 2019](https://stjepang.github.io/2019/01/29/lock-free-rust-crossbeam-in-2019.html) +* [Crossbeam Research Meta-link](https://github.com/crossbeam-rs/rfcs/wiki) + +**Projects** +* [sled](https://github.com/spacejam/sled) +* [servo](https://github.com/servo/servo) +* [TiKV](https://github.com/tikv/tikv) diff --git a/src/basics/helloworld.md b/src/basics/helloworld.md new file mode 100644 index 000000000..6a2103977 --- /dev/null +++ b/src/basics/helloworld.md @@ -0,0 +1,3 @@ +# Hello World + +* *TODO* \ No newline at end of file diff --git a/src/safety/econ_cost.md b/src/safety/econ_cost.md new file mode 100644 index 000000000..5aa79739c --- /dev/null +++ b/src/safety/econ_cost.md @@ -0,0 +1,11 @@ +# Efficiency => Security in Substrate + +> Basically I need to make the point somewhere that efficiency influences security + +We call an algorithm *efficient* if its running time is polynomial in the size of the input, and *highly efficient* if its running time is linear in the size of the input. It is important for all on-chain algorithms to be highly efficient, because they must scale linearly as the size of the Polkadot network grows. In contrast, off-chain algorithms are only required to be efficient. - [Web3 Research](http://research.web3.foundation/en/latest/polkadot/NPoS/1.intro/) + +*See [Substrate Best Practices](https://substrate.dev/docs/en/tutorials/tcr/) for more details on how efficiency influences the runtime's economic security.* + +**Related Reading** +* [Onwards; Underpriced EVM Operations](https://www.parity.io/onwards/), September 2016 +* [Under-Priced DOS Attacks on Ethereum](https://www4.comp.polyu.edu.hk/~csxluo/DoSEVM.pdf) \ No newline at end of file diff --git a/src/safety/optimizations.md b/src/safety/optimizations.md index 929f70947..8bdb1cc89 100644 --- a/src/safety/optimizations.md +++ b/src/safety/optimizations.md @@ -42,7 +42,7 @@ We call an algorithm *efficient* if its running time is polynomial in the size o Substrate developers should take advantage of Rust's zero cost abstractions. *Articles* -* [Abstraction without overhead: traits in Rust](https://rust-embedded.github.io/book/static-guarantees/zero-cost-abstractions.html) +* [Abstraction without overhead: traits in Rust](https://blog.rust-lang.org/2015/05/11/traits.html) * [Effectively Using Iterators in Rust](https://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html) * [Type States](https://rust-embedded.github.io/book/static-guarantees/zero-cost-abstractions.html) From 365ac0e1da9854b01540e71b95ff595f5bcc0175 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 23 Sep 2019 14:20:26 +0200 Subject: [PATCH 07/23] add hello world example --- README.md | 11 ++++- kitchen/modules/helloworld/Cargo.toml | 56 +++++++++++++++++++++++++ kitchen/modules/helloworld/src/lib.rs | 35 ++++++++++++++++ src/SUMMARY.md | 11 +++-- src/base/rust.md | 14 +++---- src/base/setup.md | 2 + src/basics/README.md | 59 +++++++++++++++++++++++++++ src/basics/helloworld.md | 3 -- 8 files changed, 172 insertions(+), 19 deletions(-) create mode 100644 kitchen/modules/helloworld/Cargo.toml create mode 100644 kitchen/modules/helloworld/src/lib.rs create mode 100644 src/basics/README.md delete mode 100644 src/basics/helloworld.md diff --git a/README.md b/README.md index 6e41c0a87..afe1055e9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,15 @@ Simple code patterns that demonstrate best practices when building with **[Subst This book is built with [mdbook](https://rust-lang-nursery.github.io/mdBook/continuous-integration.html) and deployed via [github pages](https://pages.github.com/). The code used to build this book is open source and [open for contributions](./CONTRIBUTING.md). ### Thanks -Thanks to the following for suggestions and content contribution: [gautamdhameja](https://github.com/gautamdhameja), [joshorndorff](https://github.com/JoshOrndorff), [joepetrowski](https://github.com/joepetrowski), [ltfschoen](https://github.com/ltfschoen), [nczhu](https://github.com/nczhu), [shawntabrizi](https://github.com/shawntabrizi), [vedant1811](https://github.com/vedant1811), [kaichao](https://github.com/kaichaosun), [4meta5](https://github.com/4meta5) +Thanks to the following for suggestions and content contribution: +* [kaichao](https://github.com/kaichaosun) +* [gautamdhameja](https://github.com/gautamdhameja) +* [joshorndorff](https://github.com/JoshOrndorff) +* [joepetrowski](https://github.com/joepetrowski) +* [ltfschoen](https://github.com/ltfschoen) +* [nczhu](https://github.com/nczhu) +* [riusricardo](https://github.com/riusricardo) +* [shawntabrizi](https://github.com/shawntabrizi) +* [vedant1811](https://github.com/vedant1811) Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. \ No newline at end of file diff --git a/kitchen/modules/helloworld/Cargo.toml b/kitchen/modules/helloworld/Cargo.toml new file mode 100644 index 000000000..e8550a320 --- /dev/null +++ b/kitchen/modules/helloworld/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "hello-world" +version = "0.1.0" +authors = ["4meta5"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'parity-scale-codec/std', + 'support/std', + 'system/std', + 'balances/std', + 'runtime-primitives/std', +] + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.5' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.runtime-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dev-dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' + +[dev-dependencies.runtime-io] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-io' +branch = 'master' diff --git a/kitchen/modules/helloworld/src/lib.rs b/kitchen/modules/helloworld/src/lib.rs new file mode 100644 index 000000000..a8c05426b --- /dev/null +++ b/kitchen/modules/helloworld/src/lib.rs @@ -0,0 +1,35 @@ +/// A very simple substrate runtime +use support::{decl_module, decl_event, decl_storage, StorageValue, StorageMap}; +use system::ensure_signed; + +pub trait Trait: system::Trait { + type Event: From> + Into<::Event>; +} + +decl_event!{ + pub enum Event where + AccountId = ::AccountId, + { + ValueSet(AccountId, u64), + } +} + +decl_storage! { + trait Store for Module as HelloWorld { + pub LastValue get(last_value): u64; + pub UserValue get(user_value): map T::AccountId => u64; + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + pub fn set_value(origin, value: u64) { + let sender = ensure_signed(origin)?; + LastValue::put(value); + UserValue::::insert(&sender, value); + Self::deposit_event(RawEvent::ValueSet(sender, value)); + } + } +} \ No newline at end of file diff --git a/src/SUMMARY.md b/src/SUMMARY.md index e5bd374ea..79f81351d 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,18 +1,17 @@ # Summary - [Introduction](./base/README.md) -- [Set Up Substrate](./base/setup.md) - [Learn Rust](./base/rust.md) -- [Hello World](./basics/hello.md) -- [Module Fundamentals](./basics/README.md) +- [Set Up](./base/setup.md) +- [Hello Substrate](./basics/README.md) - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - [Maps](./basics/list.md) - [Configurable Constants](./basics/constants.md) -- [Traits](./types/README.md) +- [Design Patterns](./types/README.md) - [Currency Types](./types/collateral.md) - - [Safety First](./safety/README.md) - - [Declarative Programming](./safety/paths.md) + - [Economic Security](./safety/README.md) + - [Condition Oriented Programming](./safety/paths.md) - [Fixed Point Arithmetic](./safety/safemath.md) - [Runtime Configuration](./base/runtime.md) - [Simple Treasury](./dao/README.md) diff --git a/src/base/rust.md b/src/base/rust.md index e71f7de13..802d921bf 100644 --- a/src/base/rust.md +++ b/src/base/rust.md @@ -5,10 +5,10 @@ To be productive with [substrate](https://github.com/substrate), it is necessary To become more familiar with commmon design patterns in Rust, see the following: * [Official Rust API Guidelines](https://rust-lang-nursery.github.io/api-guidelines/about.html) * [Rust Unofficial Design Patterns](https://github.com/rust-unofficial/patterns) -* [Elegant Library API Guidelines](https://deterministic.space/elegant-apis-in-rust.html) by Pascal Hertleif +* [Elegant Library API Guidelines](https://deterministic.space/elegant-apis-in-rust.html) To optimize runtime performance, Substrate developers should make use of iterators, traits, and Rust's other "*zero cost* abstractions": -* [Abstraction without overhead: traits in Rust](https://blog.rust-lang.org/2015/05/11/traits.html), [related conferencce talk](https://www.youtube.com/watch?v=Sn3JklPAVLk) +* [Abstraction without overhead: traits in Rust](https://blog.rust-lang.org/2015/05/11/traits.html), [related conference talk](https://www.youtube.com/watch?v=Sn3JklPAVLk) * [Effectively Using Iterators in Rust](https://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html), *[iterate over a slice rather than a `vec!`](https://twitter.com/heinz_gies/status/1121490424739303425)* * [Achieving Warp Speed with Rust](http://troubles.md/posts/rust-optimization/) @@ -19,11 +19,7 @@ For a high-level overview of concurrency in Rust, Stjepan Glavina provides the f * **[Tokio](https://github.com/tokio-rs/tokio)** runs tasks which sometimes need to be paused in order to wait for asynchronous events. Handling tons of such tasks is no problem. Its goal is to distribute IO-intensive tasks onto a thread pool. * **[Crossbeam](https://github.com/crossbeam-rs/crossbeam)** is all about low-level concurrency: atomics, concurrent data structures, synchronization primitives. Same idea as the `std::sync` module, but bigger. Its goal is to provide tools on top of which libraries like Rayon and Tokio can be built. -To dive deeper down these rabbit holes: -* [Asynchrony](#async) -* [Concurrency](#concurrency) - -### Asynchrony +### Asynchrony [Are we `async` yet?](https://areweasyncyet.rs/) **Conceptual** @@ -36,12 +32,12 @@ To dive deeper down these rabbit holes: * [romio](https://github.com/withoutboats/romio) * [Tokio Docs](https://tokio.rs/docs/overview/) -### Concurrency +### Concurrency **Conceptual** -* [Rust Concurrency Explained](https://www.youtube.com/watch?v=Dbytx0ivH7Q) * [Lock-free Rust: Crossbeam in 2019](https://stjepang.github.io/2019/01/29/lock-free-rust-crossbeam-in-2019.html) * [Crossbeam Research Meta-link](https://github.com/crossbeam-rs/rfcs/wiki) +* [Rust Concurrency Explained](https://www.youtube.com/watch?v=Dbytx0ivH7Q) **Projects** * [sled](https://github.com/spacejam/sled) diff --git a/src/base/setup.md b/src/base/setup.md index 09584b061..ffbef0bc9 100644 --- a/src/base/setup.md +++ b/src/base/setup.md @@ -27,6 +27,8 @@ test with $ cargo test ``` +See **[Creating a Runtime Module](https://substrate.dev/docs/en/tutorials/creating-a-runtime-module)** in [the official docs](https://substrate.dev/en/tutorials). + ## Runtime To develop in the context of the runtime, clone the [substrate-node-template](https://github.com/shawntabrizi/substrate-package/tree/master/substrate-node-template) and add module logic to [`runtime/src/template.rs`](https://github.com/shawntabrizi/substrate-package/blob/master/substrate-node-template/runtime/src/template.rs). diff --git a/src/basics/README.md b/src/basics/README.md new file mode 100644 index 000000000..1ebdb9db5 --- /dev/null +++ b/src/basics/README.md @@ -0,0 +1,59 @@ +# Module Fundamentals + +Clone the [substrate module template](https://github.com/shawntabrizi/substrate-module-template): +```bash +git clone https://github.com/shawntabrizi/substrate-module-template +``` + +For an in-depth explanation of using this template, see *[Creating a Substrate Runtime Module](https://substrate.dev/docs/en/tutorials/creating-a-runtime-module)*. + +## Hello Substrate + +Import the following from [`srml-support`](https://crates.parity.io/srml_support/index.html): + +```rust +use support::{decl_module, decl_event, decl_storage, StorageValue, StorageMap}; +use system::ensure_signed; +``` + +The blockchain's runtime storage is configured in [`decl_storage`](https://crates.parity.io/srml_support/macro.decl_storage.html). + +```rust +decl_storage! { + trait Store for Module as HelloWorld { + pub LastValue get(last_value): u64; + pub UserValue get(user_value): map T::AccountId => u64; + } +} +``` + +The runtime methods defined in [`decl_module`](https://crates.parity.io/srml_support/macro.decl_module.html) are used to define permissions for interacting with runtime storage. + +```rust +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + pub fn set_value(origin, value: u64) { + let sender = ensure_signed(origin)?; + LastValue::put(value); + UserValue::::insert(&sender, value); + Self::deposit_event(RawEvent::ValueSet(sender, value)); + } + } +} +``` + +Events are declared in [`decl_event`](https://crates.parity.io/srml_support/macro.decl_event.html). The emission of events is used to determine successful execution of the logic in the body of runtime methods. + +```rust +decl_event!{ + pub enum Event where + AccountId = ::AccountId, + { + ValueSet(AccountId, u64), + } +} +``` + +*It is also possible to declare an error type for runtime modules with [`decl_error`](https://crates.parity.io/srml_support/macro.decl_error.html)* diff --git a/src/basics/helloworld.md b/src/basics/helloworld.md deleted file mode 100644 index 6a2103977..000000000 --- a/src/basics/helloworld.md +++ /dev/null @@ -1,3 +0,0 @@ -# Hello World - -* *TODO* \ No newline at end of file From 702f83fb8d71b408ef13c9fc9f0c745cfba9a485 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 23 Sep 2019 14:42:19 +0200 Subject: [PATCH 08/23] update structure --- src/README.md | 27 ++-- src/SUMMARY.md | 12 +- src/base/README.md | 7 +- src/{types => design}/README.md | 0 src/{types => design}/collateral.md | 0 src/{safety => design}/cop.md | 0 src/{safety => design}/econ_cost.md | 0 src/{safety => design}/paths.md | 0 src/{safety => design}/safemath.md | 0 .../README.md => design/safety_readme.md} | 0 src/{types => design}/structs.md | 0 src/safety/drafts/rules.md | 17 --- src/safety/drafts/visibility.md | 17 --- src/safety/optimizations.md | 118 ------------------ 14 files changed, 23 insertions(+), 175 deletions(-) rename src/{types => design}/README.md (100%) rename src/{types => design}/collateral.md (100%) rename src/{safety => design}/cop.md (100%) rename src/{safety => design}/econ_cost.md (100%) rename src/{safety => design}/paths.md (100%) rename src/{safety => design}/safemath.md (100%) rename src/{safety/README.md => design/safety_readme.md} (100%) rename src/{types => design}/structs.md (100%) delete mode 100644 src/safety/drafts/rules.md delete mode 100644 src/safety/drafts/visibility.md delete mode 100644 src/safety/optimizations.md diff --git a/src/README.md b/src/README.md index 64128067a..79f81351d 100644 --- a/src/README.md +++ b/src/README.md @@ -1,25 +1,24 @@ # Summary - [Introduction](./base/README.md) -- [Set Up Substrate](./base/setup.md) -- [Rust Review](./base/rust.md) -- [Module Fundamentals](./basics/README.md) +- [Learn Rust](./base/rust.md) +- [Set Up](./base/setup.md) +- [Hello Substrate](./basics/README.md) - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - [Maps](./basics/list.md) - [Configurable Constants](./basics/constants.md) -- [Types and Traits](./types/README.md) - - [Currency Type Usage](./types/collateral.md) - - [Safety First](./safety/README.md) - - [Declarative Programming](./safety/paths.md) +- [Design Patterns](./types/README.md) + - [Currency Types](./types/collateral.md) + - [Economic Security](./safety/README.md) + - [Condition Oriented Programming](./safety/paths.md) - [Fixed Point Arithmetic](./safety/safemath.md) -- [Runtime Configuration](./runtime/README.md) -- [Recipes](./recipes/README.md) - - [Token Curated Registry](./recipes/token.md) - - [NFT Ticket Machine](./recipes/ticket.md) - - [Proof of Existence](./recipes/poe.md) - - [Treasury](./recipes/treasury.md) - - [Social Network](./recipes/social.md) +- [Runtime Configuration](./base/runtime.md) +- [Simple Treasury](./dao/README.md) +- [Token Curated Registry](./token/README.md) +- [NFT Ticket Machine](./nft/README.md) +- [Proof of Existence](./proof/README.md) +- [Social Network](./identity/README.md) - [Featured Tutorials](./base/dessert.md) ----------- diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 79f81351d..051214605 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -2,17 +2,17 @@ - [Introduction](./base/README.md) - [Learn Rust](./base/rust.md) -- [Set Up](./base/setup.md) +- [Installation](./base/setup.md) - [Hello Substrate](./basics/README.md) - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - [Maps](./basics/list.md) - [Configurable Constants](./basics/constants.md) -- [Design Patterns](./types/README.md) - - [Currency Types](./types/collateral.md) - - [Economic Security](./safety/README.md) - - [Condition Oriented Programming](./safety/paths.md) - - [Fixed Point Arithmetic](./safety/safemath.md) +- [Best Practices](./design/README.md) + - [Types and Traits](./design/collateral.md) + - [Economic Security](./design/econsecurity.md) + - [Declarative Syntax](./design/paths.md) + - [Fixed Point Arithmetic](./design/safemath.md) - [Runtime Configuration](./base/runtime.md) - [Simple Treasury](./dao/README.md) - [Token Curated Registry](./token/README.md) diff --git a/src/base/README.md b/src/base/README.md index b07ab862a..51f69966b 100644 --- a/src/base/README.md +++ b/src/base/README.md @@ -2,10 +2,11 @@ Substrate Recipes is a collection of simple code patterns that demonstrate best practices when building blockchains with **[Substrate](https://github.com/paritytech/substrate)**. The repo used to build this book is [open source](https://github.com/substrate-developer-hub/recipes). Check out the [contributions guidelines](https://github.com/substrate-developer-hub/recipes/blob/master/CONTRIBUTING.md) for an overview of the structure and directions for getting involved. -## What is Substrate? +The current **scope** is limited to module development and runtime configuration. To learn more about Substrate, see the [official documentation](https://substrate.dev). -[Substrate](https://github.com/paritytech/substrate) is a framework for building blockchains. To learn more about Substrate, see the [official documentation](https://substrate.dev). For a high level overview, see the following blog posts: +## What is Substrate? +[Substrate](https://github.com/paritytech/substrate) is a framework for building blockchains. For a high level overview, read the following blog posts: * [What is Substrate?](https://www.parity.io/what-is-substrate/) * [Substrate in a nutshell](https://www.parity.io/substrate-in-a-nutshell/) * [A brief summary of everything Substrate and Polkadot](https://www.parity.io/a-brief-summary-of-everything-substrate-polkadot/) @@ -20,6 +21,6 @@ git clone https://github.com/substrate-developer-hub/recipes As you read through the book, practice compiling and testing recipes in [`recipes/kitchen`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen). You can't learn to code by reading about it -- play with the code in the kitchen, extract patterns, and apply them to a problem that you want to solve! -It is useful to recognize that [coding is all about abstraction](https://youtu.be/05H4YsyPA-U?t=1789). To accelerate your progress, I recommend skimming the patterns in this book, composing them into interesting projects, and building your own recipes (*and then*, pay it forward and PR the [repo](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen)!). +It is useful to recognize that [coding is all about abstraction](https://youtu.be/05H4YsyPA-U?t=1789). To accelerate your progress, I recommend skimming the patterns in this book, composing them into interesting projects, and building your own recipes. Reach out for guidance on [Stack Overflow](https://stackoverflow.com/questions/tagged/substrate) or in the [Substrate Technical Riot channel](https://riot.im/app/#/room/#substrate-technical:matrix.org). \ No newline at end of file diff --git a/src/types/README.md b/src/design/README.md similarity index 100% rename from src/types/README.md rename to src/design/README.md diff --git a/src/types/collateral.md b/src/design/collateral.md similarity index 100% rename from src/types/collateral.md rename to src/design/collateral.md diff --git a/src/safety/cop.md b/src/design/cop.md similarity index 100% rename from src/safety/cop.md rename to src/design/cop.md diff --git a/src/safety/econ_cost.md b/src/design/econ_cost.md similarity index 100% rename from src/safety/econ_cost.md rename to src/design/econ_cost.md diff --git a/src/safety/paths.md b/src/design/paths.md similarity index 100% rename from src/safety/paths.md rename to src/design/paths.md diff --git a/src/safety/safemath.md b/src/design/safemath.md similarity index 100% rename from src/safety/safemath.md rename to src/design/safemath.md diff --git a/src/safety/README.md b/src/design/safety_readme.md similarity index 100% rename from src/safety/README.md rename to src/design/safety_readme.md diff --git a/src/types/structs.md b/src/design/structs.md similarity index 100% rename from src/types/structs.md rename to src/design/structs.md diff --git a/src/safety/drafts/rules.md b/src/safety/drafts/rules.md deleted file mode 100644 index 120de0899..000000000 --- a/src/safety/drafts/rules.md +++ /dev/null @@ -1,17 +0,0 @@ -# Module Development Rules - -* add module development criteria here - -## Logic Proofs - -Because Substrate grants bare-metal control to developers, certain code patterns can expose panics at runtime. As mentioned in (2) of [Module Development Criteria](#criteria), panics can cause irreversible storage changes, possibly even bricking the blockchain and rendering it useless. - -It is the responsibility of Substrate developers to ensure that the code doesn't panics after storage changes. In many cases, safety might be independently verified by the developer while writing the code. To facilitate auditability and better testing, Substrate developers should include a proof in an `.expect()` call that shows why the code's logic is safe and will not panic. Convention dictates formatting the call like so - -```rust ->::method_call().expect("; qed"); -``` - -You can find more examples of this pattern in the [Substrate codebase](https://github.com/paritytech/substrate/search?q=expect). Indeed, including logic proofs is very important for writing readable, well-maintained code. It comes as no surprise that this pattern is also discussed in the [Substrate collectables tutorial](https://shawntabrizi.com/substrate-collectables-workshop/#/3/buying-a-kitty?id=remember-quotverify-first-write-lastquot). - -> *QED stands for Quod Erat Demonstrandum which loosely translated means "that which was to be demonstrated"* \ No newline at end of file diff --git a/src/safety/drafts/visibility.md b/src/safety/drafts/visibility.md deleted file mode 100644 index 520140dd3..000000000 --- a/src/safety/drafts/visibility.md +++ /dev/null @@ -1,17 +0,0 @@ -# Special Field Objects - - -## Public vs Private - -* and do we maintain access to all of its methods in the runtime if it's declared outside the `decl_module` block -* also for functions? - -## [COMPACT] - -When do you use compact and when do you not? What are the benefits to runtime storage and what are the implications? - -## PhantomData - -* when do we need this and what is it's use? -* `troubles.md` -* examples in the codebases (`Cumulus`, `Substrate`, `Polkadot`, `Rust-Libp2p`) \ No newline at end of file diff --git a/src/safety/optimizations.md b/src/safety/optimizations.md deleted file mode 100644 index 8bdb1cc89..000000000 --- a/src/safety/optimizations.md +++ /dev/null @@ -1,118 +0,0 @@ -# Optimization Tricks - -Runtime overhead in Substrate corresponds to the efficiency of the underlying Rust code. Therefore, it is essential to use clean, efficient Rust patterns for performance releases. This section introduces common approaches for optimizing Rust code in general and links to resources that may guide further investigation. - -* [Premature Optimization](#premature) -* [Efficiency => Security](#sec) -* [Zero-Cost Abstractions](#zero) -* [Entering `unsafe` Waters 🏴‍☠️](#unsafe) -* [Fearless Concurrency && Asynchrony](#more) - -**This section was inspired by and pulls heavily from** -* [Achieving Warp Speed with Rust](http://troubles.md/posts/rust-optimization/) by Jack Fransham, [`troubles.md`](http://troubles.md/) -* [High Performance Rust](https://www.packtpub.com/application-development/rust-high-performance) by Iban Eguia Moraza - -## Premature Optimization - -*Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.* - Page 268 of [Structured Programming with `goto` Statements](http://wiki.c2.com/?StructuredProgrammingWithGoToStatements) by Donald Knuth - -Before worrying about performance optimizations, focus on *optimizing* for readability, simplicity, and maintainability. The first step when building anything is achieving basic functionality. Only after establishing a minimal viable sample is it appropriate to consider performance-based enhancements. With that said, severe inefficiency does open attack vectors for Substrate runtimes (*see [the next section](#sec)*). Moreover, the tradeoff between optimization and simplicity is not always so clear... - -*A common misconception is that optimized code is necessarily more complicated, and that therefore optimization always represents a trade-off. However, in practice, better factored code often runs faster and uses less memory as well. In this regard, optimization is closely related to refactoring, since in both cases we are paying into the code so that we may draw back out again later if we need to.* - [src](http://wiki.c2.com/?PrematureOptimization) - -**Rust API Guidelines** -* [Official Rust API Guidelines](https://rust-lang-nursery.github.io/api-guidelines/about.html) -* [Rust Unofficial Design Patterns](https://github.com/rust-unofficial/patterns) -* [Elegant Library API Guidelines](https://deterministic.space/elegant-apis-in-rust.html) by Pascal Hertleif - -Also, use [clippy](https://github.com/rust-lang/rust-clippy)! - -## Efficiency => Security in Substrate - -We call an algorithm *efficient* if its running time is polynomial in the size of the input, and *highly efficient* if its running time is linear in the size of the input. It is important for all on-chain algorithms to be highly efficient, because they must scale linearly as the size of the Polkadot network grows. In contrast, off-chain algorithms are only required to be efficient. - [Web3 Research](http://research.web3.foundation/en/latest/polkadot/NPoS/1.intro/) - -*See [Substrate Best Practices](https://substrate.dev/docs/en/tutorials/tcr/) for more details on how efficiency influences the runtime's economic security.* - -**Related Reading** -* [Onwards; Underpriced EVM Operations](https://www.parity.io/onwards/), September 2016 -* [Under-Priced DOS Attacks on Ethereum](https://www4.comp.polyu.edu.hk/~csxluo/DoSEVM.pdf) - -## Rust Zero-Cost Abstractions - -Substrate developers should take advantage of Rust's zero cost abstractions. - -*Articles* -* [Abstraction without overhead: traits in Rust](https://blog.rust-lang.org/2015/05/11/traits.html) -* [Effectively Using Iterators in Rust](https://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html) -* [Type States](https://rust-embedded.github.io/book/static-guarantees/zero-cost-abstractions.html) - -*Tweets* -* [iterate over a slice rather than a `vec!`](https://twitter.com/heinz_gies/status/1121490424739303425) - -*Video* -* [An introduction to structs, traits, and zero-cost abstractions](https://www.youtube.com/watch?v=Sn3JklPAVLk) - -## Entering `unsafe` Waters 🏴‍☠️ - -*Please read [The Rustonomicon](https://doc.rust-lang.org/nomicon/) before experimenting with the dark magic that is `unsafe`* - -To access an element in a specific position, use the `get()` method. This method performs a double bound check. - -```rust -for arr in array_of_arrays { - if let Some(elem) = arr.iter().get(1738) { - println!("{}", elem); - } -} -``` - -The `.get()` call performs two checks: -1. checks that the index will return `Some(elem)` or `None` -2. checks that the returned element is of type `Some` or `None` - -If bound checking has already been performed independently of the call, we can invoke `.getunchecked()` to access the element. Although this is `unsafe` to use, it is equivalent to C/C++ indexing, thereby improving performance when we already know the element's location. - -```rust -for arr in array_of_arrays { - println!("{}", unsafe { arr.get_unchecked(1738) }) -} -``` - -**NOTE**: if we don't verify the input to `.getunchecked()`, the caller may access whatever is stored in the location even if it is a memory address outside the slice - -## Fearless Concurrency && Asynchrony - -As a systems programming language, Rust provides significant flexibility with respect to low-level optimizations. Specifically, Rust provides fine-grain control over how you perform computation, delegate said computation to the OS's threads, and schedule state transitions within a given thread. There isn't space in this book to go into significant detail, but I'll try to provide resources/reading that have helped me get up to speed. For a high-level overview, Stjepan Glavina provides the following descriptions in [Lock-free Rust: Crossbeam in 2019](https://stjepang.github.io/2019/01/29/lock-free-rust-crossbeam-in-2019.html): - -* **[Rayon](https://github.com/rayon-rs/rayon)** splits your data into distinct pieces, gives each piece to a thread to do some kind of computation on it, and finally aggregates results. Its goal is to distribute CPU-intensive tasks onto a thread pool. -* **[Tokio](https://github.com/tokio-rs/tokio)** runs tasks which sometimes need to be paused in order to wait for asynchronous events. Handling tons of such tasks is no problem. Its goal is to distribute IO-intensive tasks onto a thread pool. -* **[Crossbeam](https://github.com/crossbeam-rs/crossbeam)** is all about low-level concurrency: atomics, concurrent data structures, synchronization primitives. Same idea as the `std::sync` module, but bigger. Its goal is to provide tools on top of which libraries like Rayon and Tokio can be built. - -To dive deeper down these 🐰 holes -* [Asynchrony](#async) -* [Concurrency](#concurrency) - -### Asynchrony -[Are we `async` yet?](https://areweasyncyet.rs/) - -**Conceptual** -* [RustLatam 2019 - Without Boats: Zero-Cost Async IO](https://www.youtube.com/watch?v=skos4B5x7qE) -* [Introduction to Async/Await Programming (withoutboats/wakers-i):](https://boats.gitlab.io/blog/post/wakers-i/) -* [Futures (by Aaron Turon)](http://aturon.github.io/2016/08/11/futures/) - -**Projects** -* [Rust Asynchronous Ecosystem Working Group](https://github.com/rustasync) -* [romio](https://github.com/withoutboats/romio) -* [Tokio Docs](https://tokio.rs/docs/overview/) - -### Concurrency - -**Conceptual** -* [Rust Concurrency Explained](https://www.youtube.com/watch?v=Dbytx0ivH7Q) -* [Lock-free Rust: Crossbeam in 2019](https://stjepang.github.io/2019/01/29/lock-free-rust-crossbeam-in-2019.html) -* [Crossbeam Research Meta-link](https://github.com/crossbeam-rs/rfcs/wiki) - -**Projects** -* [sled](https://github.com/spacejam/sled) -* [servo](https://github.com/servo/servo) -* [TiKV](https://github.com/tikv/tikv) \ No newline at end of file From 34881666d1754798fb3ffcdcf4e377ae36517eb2 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 23 Sep 2019 16:02:13 +0200 Subject: [PATCH 09/23] fix recognition --- kitchen/modules/helloworld/Cargo.toml | 2 +- src/tour/README.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/kitchen/modules/helloworld/Cargo.toml b/kitchen/modules/helloworld/Cargo.toml index e8550a320..16549b59c 100644 --- a/kitchen/modules/helloworld/Cargo.toml +++ b/kitchen/modules/helloworld/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hello-world" version = "0.1.0" -authors = ["4meta5"] +authors = ["shawntabrizi"] edition = "2018" [features] diff --git a/src/tour/README.md b/src/tour/README.md index 731c3cc03..c8526c2f5 100644 --- a/src/tour/README.md +++ b/src/tour/README.md @@ -1,5 +1,7 @@ # SRML Tour +(**deprecated**) + [srml-tour](https://github.com/JoshOrndorff/srml-tour) intends to explain the features of SRML modules, demonstrate use cases, and explore the code. It is *in progress*, tracked in [issues](https://github.com/substrate-developer-hub/recipes/issues). ## smpl-treasury From c5877267810de63895a6aeab09cf0153c534f5f8 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Wed, 25 Sep 2019 13:22:33 +0200 Subject: [PATCH 10/23] minimal event examples --- kitchen/.DS_Store | Bin 0 -> 6148 bytes kitchen/modules/generic-event/Cargo.toml | 56 +++++++++++++++++++++++ kitchen/modules/generic-event/src/lib.rs | 32 +++++++++++++ kitchen/modules/simple-event/Cargo.toml | 56 +++++++++++++++++++++++ kitchen/modules/simple-event/src/lib.rs | 33 +++++++++++++ 5 files changed, 177 insertions(+) create mode 100644 kitchen/.DS_Store create mode 100644 kitchen/modules/generic-event/Cargo.toml create mode 100644 kitchen/modules/generic-event/src/lib.rs create mode 100644 kitchen/modules/simple-event/Cargo.toml create mode 100644 kitchen/modules/simple-event/src/lib.rs diff --git a/kitchen/.DS_Store b/kitchen/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c80bcc39955d8f02cb1f05b8788e073a97ab36c5 GIT binary patch literal 6148 zcmeHKJ8DBg3>+nf7}B^*xmWNF7Gs~l7f4}~#ve#<>R07lKAJ}#g3WW9K$2`hLQ>PuSy#4Nlkm z!&}~ZpQfx7kOERb3P=Gd@Jj`}_tK`%iHcG{3P^!(1^oNa=#IT`NQ_ShLyQ2#3DaR* z$1Fi?o*?$ZA(0uHC6$;|s}aMJ&U~x7UN|Hs9ah7K)sw9z6pN?x{ubr1o~S4Vq`<8L zkGY+B|6kF6nE!7{+DQQ^@UIlG$#T71@Rh2!j$Y1tZKJ=^z2;1J<2on|(T<7Hj=Ax6 e{1Qc3*L=^)ukQ$fUquD{uh|X%(;l literal 0 HcmV?d00001 diff --git a/kitchen/modules/generic-event/Cargo.toml b/kitchen/modules/generic-event/Cargo.toml new file mode 100644 index 000000000..bb84b958d --- /dev/null +++ b/kitchen/modules/generic-event/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "generic-event" +version = "0.1.0" +authors = ["4meta5"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'parity-scale-codec/std', + 'support/std', + 'system/std', + 'balances/std', + 'runtime-primitives/std', +] + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.5' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.runtime-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dev-dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' + +[dev-dependencies.runtime-io] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-io' +branch = 'master' diff --git a/kitchen/modules/generic-event/src/lib.rs b/kitchen/modules/generic-event/src/lib.rs new file mode 100644 index 000000000..13a6b304a --- /dev/null +++ b/kitchen/modules/generic-event/src/lib.rs @@ -0,0 +1,32 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Event generic over multiple types (u32, AccountId) +use support::{decl_event, decl_module, dispatch::Result}; +use system::ensure_signed; + +pub trait Trait: system::Trait { + type Event: From> + Into<::Event>; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + fn do_something(origin, input: u32) -> Result { + let user = ensure_signed(origin)?; + + // could do something with the input here instead + let new_number = input; + + Self::deposit_event(RawEvent::EmitInput(user, new_number)); + Ok(()) + } + } +} + +// AccountId, u32 both are inputs `=>` generic event declaration +decl_event!( + pub enum Event where AccountId = ::AccountId { + EmitInput(AccountId, u32), + } +); diff --git a/kitchen/modules/simple-event/Cargo.toml b/kitchen/modules/simple-event/Cargo.toml new file mode 100644 index 000000000..1e7735120 --- /dev/null +++ b/kitchen/modules/simple-event/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "simple-event" +version = "0.1.0" +authors = ["4meta5"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'parity-scale-codec/std', + 'support/std', + 'system/std', + 'balances/std', + 'runtime-primitives/std', +] + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.5' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.runtime-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dev-dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' + +[dev-dependencies.runtime-io] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-io' +branch = 'master' diff --git a/kitchen/modules/simple-event/src/lib.rs b/kitchen/modules/simple-event/src/lib.rs new file mode 100644 index 000000000..f12ab2317 --- /dev/null +++ b/kitchen/modules/simple-event/src/lib.rs @@ -0,0 +1,33 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Simple Event (not generic over types) +use support::{decl_event, decl_module, dispatch::Result}; +use system::ensure_signed; + +pub trait Trait: system::Trait { + type Event: From + Into<::Event>; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + fn do_something(origin, input: u32) -> Result { + let _ = ensure_signed(origin)?; + + // could do something with the input here instead + let new_number = input; + + // emit event + Self::deposit_event(Event::EmitInput(new_number)); + Ok(()) + } + } +} + +// only uses u32 so does not need to be generic +decl_event!( + pub enum Event { + EmitInput(u32), + } +); From 0100846d60851148961ead02462666249db47b52 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Wed, 25 Sep 2019 14:47:12 +0200 Subject: [PATCH 11/23] simple map --- kitchen/README.md | 29 +- kitchen/modules/README.md | 26 ++ .../{helloworld => hellosubstrate}/Cargo.toml | 2 +- .../{helloworld => hellosubstrate}/src/lib.rs | 0 kitchen/modules/simple-map/Cargo.toml | 56 ++++ kitchen/modules/simple-map/src/lib.rs | 86 +++++ kitchen/modules/value/src/lib.rs | 4 +- kitchen/runtimes/README.md | 5 + kitchen/runtimes/supernode/.gitignore | 6 - kitchen/runtimes/supernode/Cargo.toml | 83 ----- kitchen/runtimes/supernode/LICENSE | 24 -- kitchen/runtimes/supernode/README.md | 68 ---- kitchen/runtimes/supernode/build.rs | 8 - kitchen/runtimes/supernode/runtime/Cargo.toml | 158 ---------- kitchen/runtimes/supernode/runtime/src/lib.rs | 296 ------------------ .../supernode/runtime/wasm/Cargo.toml | 21 -- .../runtimes/supernode/runtime/wasm/build.sh | 13 - .../supernode/runtime/wasm/src/lib.rs | 5 - kitchen/runtimes/supernode/scripts/build.sh | 26 -- kitchen/runtimes/supernode/scripts/init.sh | 16 - kitchen/runtimes/supernode/src/chain_spec.rs | 119 ------- kitchen/runtimes/supernode/src/cli.rs | 94 ------ kitchen/runtimes/supernode/src/error.rs | 13 - kitchen/runtimes/supernode/src/main.rs | 25 -- kitchen/runtimes/supernode/src/service.rs | 115 ------- 25 files changed, 180 insertions(+), 1118 deletions(-) create mode 100644 kitchen/modules/README.md rename kitchen/modules/{helloworld => hellosubstrate}/Cargo.toml (97%) rename kitchen/modules/{helloworld => hellosubstrate}/src/lib.rs (100%) create mode 100644 kitchen/modules/simple-map/Cargo.toml create mode 100644 kitchen/modules/simple-map/src/lib.rs create mode 100644 kitchen/runtimes/README.md delete mode 100644 kitchen/runtimes/supernode/.gitignore delete mode 100644 kitchen/runtimes/supernode/Cargo.toml delete mode 100644 kitchen/runtimes/supernode/LICENSE delete mode 100644 kitchen/runtimes/supernode/README.md delete mode 100644 kitchen/runtimes/supernode/build.rs delete mode 100644 kitchen/runtimes/supernode/runtime/Cargo.toml delete mode 100644 kitchen/runtimes/supernode/runtime/src/lib.rs delete mode 100644 kitchen/runtimes/supernode/runtime/wasm/Cargo.toml delete mode 100755 kitchen/runtimes/supernode/runtime/wasm/build.sh delete mode 100644 kitchen/runtimes/supernode/runtime/wasm/src/lib.rs delete mode 100755 kitchen/runtimes/supernode/scripts/build.sh delete mode 100755 kitchen/runtimes/supernode/scripts/init.sh delete mode 100644 kitchen/runtimes/supernode/src/chain_spec.rs delete mode 100644 kitchen/runtimes/supernode/src/cli.rs delete mode 100644 kitchen/runtimes/supernode/src/error.rs delete mode 100644 kitchen/runtimes/supernode/src/main.rs delete mode 100644 kitchen/runtimes/supernode/src/service.rs diff --git a/kitchen/README.md b/kitchen/README.md index 959188924..67f791858 100644 --- a/kitchen/README.md +++ b/kitchen/README.md @@ -1,31 +1,10 @@ # Kitchen -The kitchen is for *cooking* recipes. It is structured like the main recipes build as specified in [src/SUMMARY.md](../src/SUMMARY.md), except every code sample is stored as a library via the [substrate-module-template](https://github.com/shawntabrizi/substrate-module-template). +The kitchen is for *cooking* recipes. It is structured similarly to the main recipes build as specified in [src/SUMMARY.md](../src/SUMMARY.md), except every code sample is stored as a library via the [substrate-module-template](https://github.com/shawntabrizi/substrate-module-template). -**NEW STRUCTURE** -* divided up into `modules` and `runtimes`... - -**Event**: effectively logging, scheduling, and reacting to events defined in the `decl_event` block of the runtime. -* [Adding Machine](./adder/) - -**Storage**: managing interactions with the on-chain storage via the `decl_storage` block in the runtime. -* [Single Value Storage](./value/) -* [Configurable Module Constants](./constants/) -* [Lists as Maps](./list/) - -**MISC** -- [Nested Structs](./nstructs) -- Currency Types and Locking Techniques::{[lockable](./lockable), [reservable](./reservable), [imbalances](./imbalances)} -- [Token Transfer](./token) -- [Permissioned Methods](./permissioned) -- [Blockchain Event Loop](./loop) -- [Social Network](./social) - - +There are two main sections: +* [Modules](./modules/README.md): individual modules, formatted as libraries +* [Runtimes](./runtimes/README.md): module configurations for executable runtimes ## Directions diff --git a/kitchen/modules/README.md b/kitchen/modules/README.md new file mode 100644 index 000000000..6078195e5 --- /dev/null +++ b/kitchen/modules/README.md @@ -0,0 +1,26 @@ +# Modules + +**Event**: effectively logging, scheduling, and reacting to events defined in `decl_event` +* [Adding Machine](./modules/adder/) +* [Simple Event (not generic)](./modules/simple-event) +* [Generic Event](./modules/generic-event) + +**Storage**: managing interactions with the on-chain storage via `decl_storage` +* [Single Value Storage](./modules/value) +* [Simple Map](./modules/simple-map) +* [List](./modules/list) +* [Double Map](./modules/double-map) +* [Child Trie](./modules/child-trie) +* [Offchain Workers](./modules/offchain-workers) + +**Traits and Types**: using substrate traits and types +* [Module Inheritance](./modules/inherit) +* [Configurable Module Constants](./modules/constants/) +- [Nested Structs](./nstructs) + +**Examples**: usage examples of the above patterns *with context* +- Currency Types and Locking Techniques::{[lockable](./lockable), [reservable](./reservable), [imbalances](./imbalances)} +- [Token Transfer](./token) +- [Permissioned Methods](./permissioned) +- [Blockchain Event Loop](./loop) +- [Social Network](./social) \ No newline at end of file diff --git a/kitchen/modules/helloworld/Cargo.toml b/kitchen/modules/hellosubstrate/Cargo.toml similarity index 97% rename from kitchen/modules/helloworld/Cargo.toml rename to kitchen/modules/hellosubstrate/Cargo.toml index 16549b59c..d54573f10 100644 --- a/kitchen/modules/helloworld/Cargo.toml +++ b/kitchen/modules/hellosubstrate/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "hello-world" +name = "hello-substrate" version = "0.1.0" authors = ["shawntabrizi"] edition = "2018" diff --git a/kitchen/modules/helloworld/src/lib.rs b/kitchen/modules/hellosubstrate/src/lib.rs similarity index 100% rename from kitchen/modules/helloworld/src/lib.rs rename to kitchen/modules/hellosubstrate/src/lib.rs diff --git a/kitchen/modules/simple-map/Cargo.toml b/kitchen/modules/simple-map/Cargo.toml new file mode 100644 index 000000000..4e8fc217a --- /dev/null +++ b/kitchen/modules/simple-map/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "simple-map" +version = "0.1.0" +authors = ["4meta5"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'parity-scale-codec/std', + 'support/std', + 'system/std', + 'balances/std', + 'runtime-primitives/std', +] + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.5' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.runtime-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dev-dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' + +[dev-dependencies.runtime-io] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-io' +branch = 'master' diff --git a/kitchen/modules/simple-map/src/lib.rs b/kitchen/modules/simple-map/src/lib.rs new file mode 100644 index 000000000..6aef64be6 --- /dev/null +++ b/kitchen/modules/simple-map/src/lib.rs @@ -0,0 +1,86 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// Simple Storage Map +// https://crates.parity.io/srml_support/storage/trait.StorageMap.html +use support::{ensure, decl_module, decl_storage, decl_event, StorageMap, dispatch::Result}; +use system::ensure_signed; + +pub trait Trait: system::Trait { + type Event: From> + Into<::Event>; +} + +decl_storage! { + trait Store for Module as SimpleMap { + SimpleMap get(simple_map): map T::AccountId => u32; + } +} + +decl_event!( + pub enum Event where AccountId = ::AccountId { + // insert new entry + EntrySet(AccountId, u32), + EntryGot(AccountId, u32), + EntryTook(AccountId, u32), + } +); + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + fn set_single_entry(origin, entry: u32) -> Result { + // only a user can set their entry + let user = ensure_signed(origin)?; + + >::insert(user.clone(), entry); + + Self::deposit_event(RawEvent::EntrySet(user, entry)); + Ok(()) + } + + fn get_single_entry(origin, account: T::AccountId) -> Result { + // anyone (signed extrinsic) can get an entry + let getter = ensure_signed(origin)?; + + ensure!(>::exists(account.clone()), "an entry does not exist for this user"); + let entry = >::get(account); + Self::deposit_event(RawEvent::EntryGot(getter, entry)); + Ok(()) + } + + fn take_single_entry(origin) -> Result { + // only the user can take their own entry + let user = ensure_signed(origin)?; + + ensure!(>::exists(user.clone()), "an entry does not exist for this user"); + let entry = >::take(user.clone()); + // ensure!(!>::exists(user.clone()), "the take did not succeed"); + Self::deposit_event(RawEvent::EntryTook(user, entry)); + Ok(()) + } + + fn mutate_single_entry(origin, add_this_val: u32) -> Result { + // only the user can mutate their own entry + let user = ensure_signed(origin)?; + + // adds `add_this_val` to the entry + >::mutate(user.clone(), |entry| *entry += add_this_val); + // warning: does NOT check for overflow + + Ok(()) + } + + fn compare_and_swap_single_entry(origin, old_entry: u32, new_entry: u32) -> Result { + // only a user that knows their previous entry can set the new entry + let user = ensure_signed(origin)?; + + // compare + ensure!(old_entry == >::get(user.clone()), "cas failed because old_entry == existing_entry"); + // and swap + >::insert(user, new_entry); + Ok(()) + } + + // TODO: `append` and `append_or_insert` + } +} \ No newline at end of file diff --git a/kitchen/modules/value/src/lib.rs b/kitchen/modules/value/src/lib.rs index 0c9097c8a..420f5f9cf 100644 --- a/kitchen/modules/value/src/lib.rs +++ b/kitchen/modules/value/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] /// Single Value Storage -use support::{decl_module, decl_storage, dispatch::Result, ensure, StorageValue}; +use support::{decl_module, decl_storage, dispatch::Result, StorageValue}; use system::ensure_signed; pub trait Trait: system::Trait {} @@ -16,7 +16,7 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { fn set_value(origin, value: u32) -> Result { // check sender signature to verify permissions - let sender = ensure_signed(origin)?; + let _ = ensure_signed(origin)?; ::put(value); Ok(()) } diff --git a/kitchen/runtimes/README.md b/kitchen/runtimes/README.md new file mode 100644 index 000000000..46395f2f1 --- /dev/null +++ b/kitchen/runtimes/README.md @@ -0,0 +1,5 @@ +# Runtimes + +* super-node +* loosely coupled modules +* larger examples of inter-module usage `=>` look at Basti's PR and push the limits of how many modules can be used in a runtime \ No newline at end of file diff --git a/kitchen/runtimes/supernode/.gitignore b/kitchen/runtimes/supernode/.gitignore deleted file mode 100644 index b6e870080..000000000 --- a/kitchen/runtimes/supernode/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -**/target/ - -# These are backup files generated by rustfmt -**/*.rs.bk diff --git a/kitchen/runtimes/supernode/Cargo.toml b/kitchen/runtimes/supernode/Cargo.toml deleted file mode 100644 index 4f9343d18..000000000 --- a/kitchen/runtimes/supernode/Cargo.toml +++ /dev/null @@ -1,83 +0,0 @@ -[[bin]] -name = 'node-template' -path = 'src/main.rs' -[profile.release] -panic = 'unwind' - -[dependencies] -error-chain = '0.12' -exit-future = '0.1' -futures = '0.1' -hex-literal = '0.1' -log = '0.4' -parity-codec = '3.2' -parking_lot = '0.7.1' -tokio = '0.1' -trie-root = '0.12.0' - -[dependencies.basic-authorship] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-basic-authorship' -branch = 'v1.0' - -[dependencies.consensus] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-consensus-aura' -branch = 'v1.0' - -[dependencies.ctrlc] -features = ['termination'] -version = '3.0' - -[dependencies.inherents] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-inherents' -branch = 'v1.0' - -[dependencies.network] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-network' -branch = 'v1.0' - -[dependencies.node-template-runtime] -path = 'runtime' - -[dependencies.primitives] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-primitives' -branch = 'v1.0' - -[dependencies.sr-io] -git = 'https://github.com/paritytech/substrate.git' -branch = 'v1.0' - -[dependencies.substrate-cli] -git = 'https://github.com/paritytech/substrate.git' -branch = 'v1.0' - -[dependencies.substrate-client] -git = 'https://github.com/paritytech/substrate.git' -branch = 'v1.0' - -[dependencies.substrate-executor] -git = 'https://github.com/paritytech/substrate.git' -branch = 'v1.0' - -[dependencies.substrate-service] -git = 'https://github.com/paritytech/substrate.git' -branch = 'v1.0' - -[dependencies.transaction-pool] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-transaction-pool' -branch = 'v1.0' - -[package] -authors = ['Parity Technologies '] -build = 'build.rs' -edition = '2018' -name = 'node-template' -version = '1.0.0' - -[build-dependencies] -vergen = '3' diff --git a/kitchen/runtimes/supernode/LICENSE b/kitchen/runtimes/supernode/LICENSE deleted file mode 100644 index cf1ab25da..000000000 --- a/kitchen/runtimes/supernode/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to diff --git a/kitchen/runtimes/supernode/README.md b/kitchen/runtimes/supernode/README.md deleted file mode 100644 index 2989d6e37..000000000 --- a/kitchen/runtimes/supernode/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# Super Node - -A node with all the module configurations from [modules](./modules) - -# Building - -Install Rust: - -```bash -curl https://sh.rustup.rs -sSf | sh -``` - -Install required tools: - -```bash -./scripts/init.sh -``` - -Build the WebAssembly binary: - -```bash -./scripts/build.sh -``` - -Build all native code: - -```bash -cargo build -``` - -# Run - -You can start a development chain with: - -```bash -cargo run -- --dev -``` - -Detailed logs may be shown by running the node with the following environment variables set: `RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`. - -If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with testnet units. Give each node a name and expose them so they are listed on the Polkadot [telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open. - -We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at `/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from the `--node-key` value that we specify below: - -```bash -cargo run -- \ - --base-path /tmp/alice \ - --chain=local \ - --alice \ - --node-key 0000000000000000000000000000000000000000000000000000000000000001 \ - --telemetry-url ws://telemetry.polkadot.io:1024 \ - --validator -``` - -In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's bootnode ID on TCP port 30333: - -```bash -cargo run -- \ - --base-path /tmp/bob \ - --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \ - --chain=local \ - --bob \ - --port 30334 \ - --telemetry-url ws://telemetry.polkadot.io:1024 \ - --validator -``` - -Additional CLI usage options are available and may be shown by running `cargo run -- --help`. diff --git a/kitchen/runtimes/supernode/build.rs b/kitchen/runtimes/supernode/build.rs deleted file mode 100644 index afc39d3b6..000000000 --- a/kitchen/runtimes/supernode/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -use vergen::{ConstantsFlags, generate_cargo_keys}; - -const ERROR_MSG: &str = "Failed to generate metadata files"; - -fn main() { - generate_cargo_keys(ConstantsFlags::all()).expect(ERROR_MSG); - println!("cargo:rerun-if-changed=.git/HEAD"); -} diff --git a/kitchen/runtimes/supernode/runtime/Cargo.toml b/kitchen/runtimes/supernode/runtime/Cargo.toml deleted file mode 100644 index d71382e89..000000000 --- a/kitchen/runtimes/supernode/runtime/Cargo.toml +++ /dev/null @@ -1,158 +0,0 @@ -[features] -default = ['std'] -std = [ - 'parity-codec/std', - 'primitives/std', - 'client/std', - 'rstd/std', - 'runtime-io/std', - 'support/std', - 'balances/std', - 'executive/std', - 'aura/std', - 'indices/std', - 'primitives/std', - 'system/std', - 'timestamp/std', - 'sudo/std', - 'version/std', - 'serde', - 'safe-mix/std', - 'consensus-aura/std', - 'offchain-primitives/std', - 'event-adder/std', -] - -[dependencies.event-adder] -default_features = false -path = '../../event/adder/' -package = 'event-adder' -branch = 'v1.0' - -[dependencies.aura] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-aura' -branch = 'v1.0' - -[dependencies.balances] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-balances' -branch = 'v1.0' - -[dependencies.client] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-client' -branch = 'v1.0' - -[dependencies.consensus] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-consensus' -branch = 'v1.0' - -[dependencies.consensus-aura] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-consensus-aura-primitives' -branch = 'v1.0' - -[dependencies.consensus_authorities] -default-features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-consensus-authorities' -branch = 'v1.0' - -[dependencies.executive] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-executive' -branch = 'v1.0' - -[dependencies.indices] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-indices' -branch = 'v1.0' - -[dependencies.offchain-primitives] -default-features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-offchain-primitives' -branch = 'v1.0' - -[dependencies.parity-codec] -default-features = false -features = ['derive'] -version = '3.5' - -[dependencies.primitives] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-primitives' -branch = 'v1.0' - -[dependencies.rstd] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'sr-std' -branch = 'v1.0' - -[dependencies.runtime-io] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'sr-io' -branch = 'v1.0' - -[dependencies.runtime-primitives] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'sr-primitives' -branch = 'v1.0' - -[dependencies.safe-mix] -default-features = false -version = '1.0' - -[dependencies.serde] -features = ['derive'] -optional = true -version = '1.0' - -[dependencies.sudo] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-sudo' -branch = 'v1.0' - -[dependencies.support] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-support' -branch = 'v1.0' - -[dependencies.system] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-system' -branch = 'v1.0' - -[dependencies.timestamp] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-timestamp' -branch = 'v1.0' - -[dependencies.version] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'sr-version' -branch = 'v1.0' - -[package] -authors = ['Parity Technologies '] -edition = '2018' -name = 'node-template-runtime' -version = '1.0.0' diff --git a/kitchen/runtimes/supernode/runtime/src/lib.rs b/kitchen/runtimes/supernode/runtime/src/lib.rs deleted file mode 100644 index 4f3bffdcb..000000000 --- a/kitchen/runtimes/supernode/runtime/src/lib.rs +++ /dev/null @@ -1,296 +0,0 @@ -//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit="256"] - -#[cfg(feature = "std")] -use serde::{Serialize, Deserialize}; -use parity_codec::{Encode, Decode}; -use rstd::prelude::*; -#[cfg(feature = "std")] -use primitives::bytes; -use primitives::{ed25519, sr25519, OpaqueMetadata}; -use runtime_primitives::{ - ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, - traits::{self, NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify} -}; -use client::{ - block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api}, - runtime_api, impl_runtime_apis -}; -use version::RuntimeVersion; -#[cfg(feature = "std")] -use version::NativeVersion; - -// A few exports that help ease life for downstream crates. -#[cfg(any(feature = "std", test))] -pub use runtime_primitives::BuildStorage; -pub use consensus::Call as ConsensusCall; -pub use timestamp::Call as TimestampCall; -pub use balances::Call as BalancesCall; -pub use runtime_primitives::{Permill, Perbill}; -pub use timestamp::BlockPeriod; -pub use support::{StorageValue, construct_runtime}; - -/// The type that is used for identifying authorities. -pub type AuthorityId = ::Signer; - -/// The type used by authorities to prove their ID. -pub type AuthoritySignature = ed25519::Signature; - -/// Alias to pubkey that identifies an account on the chain. -pub type AccountId = ::Signer; - -/// The type used by authorities to prove their ID. -pub type AccountSignature = sr25519::Signature; - -/// A hash of some data used by the chain. -pub type Hash = primitives::H256; - -/// Index of a block number in the chain. -pub type BlockNumber = u64; - -/// Index of an account's extrinsic in the chain. -pub type Nonce = u64; - -/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know -/// the specifics of the runtime. They can then be made to be agnostic over specific formats -/// of data like extrinsics, allowing for them to continue syncing the network through upgrades -/// to even the core datastructures. -pub mod opaque { - use super::*; - - /// Opaque, encoded, unchecked extrinsic. - #[derive(PartialEq, Eq, Clone, Default, Encode, Decode)] - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - pub struct UncheckedExtrinsic(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); - #[cfg(feature = "std")] - impl std::fmt::Debug for UncheckedExtrinsic { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "{}", primitives::hexdisplay::HexDisplay::from(&self.0)) - } - } - impl traits::Extrinsic for UncheckedExtrinsic { - fn is_signed(&self) -> Option { - None - } - } - /// Opaque block header type. - pub type Header = generic::Header>; - /// Opaque block type. - pub type Block = generic::Block; - /// Opaque block identifier type. - pub type BlockId = generic::BlockId; - /// Opaque session key type. - pub type SessionKey = AuthorityId; -} - -/// This runtime version. -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("node-template"), - impl_name: create_runtime_str!("node-template"), - authoring_version: 3, - spec_version: 4, - impl_version: 4, - apis: RUNTIME_API_VERSIONS, -}; - -/// The version infromation used to identify this runtime when compiled natively. -#[cfg(feature = "std")] -pub fn native_version() -> NativeVersion { - NativeVersion { - runtime_version: VERSION, - can_author_with: Default::default(), - } -} - -impl system::Trait for Runtime { - /// The identifier used to distinguish between accounts. - type AccountId = AccountId; - /// The lookup mechanism to get account ID from whatever is passed in dispatchers. - type Lookup = Indices; - /// The index type for storing how many extrinsics an account has signed. - type Index = Nonce; - /// The index type for blocks. - type BlockNumber = BlockNumber; - /// The type for hashing blocks and tries. - type Hash = Hash; - /// The hashing algorithm used. - type Hashing = BlakeTwo256; - /// The header digest type. - type Digest = generic::Digest; - /// The header type. - type Header = generic::Header; - /// The ubiquitous event type. - type Event = Event; - /// The ubiquitous log type. - type Log = Log; - /// The ubiquitous origin type. - type Origin = Origin; -} - -impl aura::Trait for Runtime { - type HandleReport = (); -} - -impl consensus::Trait for Runtime { - /// The identifier we use to refer to authorities. - type SessionKey = AuthorityId; - // The aura module handles offline-reports internally - // rather than using an explicit report system. - type InherentOfflineReport = (); - /// The ubiquitous log type. - type Log = Log; -} - -impl indices::Trait for Runtime { - /// The type for recording indexing into the account enumeration. If this ever overflows, there - /// will be problems! - type AccountIndex = u32; - /// Use the standard means of resolving an index hint from an id. - type ResolveHint = indices::SimpleResolveHint; - /// Determine whether an account is dead. - type IsDeadAccount = Balances; - /// The uniquitous event type. - type Event = Event; -} - -impl timestamp::Trait for Runtime { - /// A timestamp: seconds since the unix epoch. - type Moment = u64; - type OnTimestampSet = Aura; -} - -impl balances::Trait for Runtime { - /// The type for recording an account's balance. - type Balance = u128; - /// What to do if an account's free balance gets zeroed. - type OnFreeBalanceZero = (); - /// What to do if a new account is created. - type OnNewAccount = Indices; - /// The uniquitous event type. - type Event = Event; - - type TransactionPayment = (); - type DustRemoval = (); - type TransferPayment = (); -} - -impl sudo::Trait for Runtime { - /// The uniquitous event type. - type Event = Event; - type Proposal = Call; -} - -impl event_adder::Trait for Runtime { - type Event = Event; -} - -construct_runtime!( - pub enum Runtime with Log(InternalLog: DigestItem) where - Block = Block, - NodeBlock = opaque::Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: system::{default, Log(ChangesTrieRoot)}, - Timestamp: timestamp::{Module, Call, Storage, Config, Inherent}, - Consensus: consensus::{Module, Call, Storage, Config, Log(AuthoritiesChange), Inherent}, - Aura: aura::{Module}, - Indices: indices, - Balances: balances, - Sudo: sudo, - EventAdder: event_adder::{Module, Call, Event}, - } -); - -/// The type used as a helper for interpreting the sender of transactions. -type Context = system::ChainContext; -/// The address format for describing accounts. -type Address = ::Source; -/// Block header type as expected by this runtime. -pub type Header = generic::Header; -/// Block type as expected by this runtime. -pub type Block = generic::Block; -/// BlockId type as expected by this runtime. -pub type BlockId = generic::BlockId; -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; -/// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; -/// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; - -// Implement our runtime API endpoints. This is just a bunch of proxying. -impl_runtime_apis! { - impl runtime_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - - fn execute_block(block: Block) { - Executive::execute_block(block) - } - - fn initialize_block(header: &::Header) { - Executive::initialize_block(header) - } - - fn authorities() -> Vec { - panic!("Deprecated, please use `AuthoritiesApi`.") - } - } - - impl runtime_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - Runtime::metadata().into() - } - } - - impl block_builder_api::BlockBuilder for Runtime { - fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyResult { - Executive::apply_extrinsic(extrinsic) - } - - fn finalize_block() -> ::Header { - Executive::finalize_block() - } - - fn inherent_extrinsics(data: InherentData) -> Vec<::Extrinsic> { - data.create_extrinsics() - } - - fn check_inherents(block: Block, data: InherentData) -> CheckInherentsResult { - data.check_extrinsics(&block) - } - - fn random_seed() -> ::Hash { - System::random_seed() - } - } - - impl runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction(tx: ::Extrinsic) -> TransactionValidity { - Executive::validate_transaction(tx) - } - } - - impl consensus_aura::AuraApi for Runtime { - fn slot_duration() -> u64 { - Aura::slot_duration() - } - } - - impl offchain_primitives::OffchainWorkerApi for Runtime { - fn offchain_worker(n: NumberFor) { - Executive::offchain_worker(n) - } - } - - impl consensus_authorities::AuthoritiesApi for Runtime { - fn authorities() -> Vec { - Consensus::authorities() - } - } -} diff --git a/kitchen/runtimes/supernode/runtime/wasm/Cargo.toml b/kitchen/runtimes/supernode/runtime/wasm/Cargo.toml deleted file mode 100644 index 65290882d..000000000 --- a/kitchen/runtimes/supernode/runtime/wasm/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[lib] -crate-type = ['cdylib'] - -[workspace] -members = [] -[profile.release] -lto = true -panic = 'abort' - -[features] -default = [] -std = ['node-template-runtime/std'] -[dependencies.node-template-runtime] -default-features = false -path = '..' - -[package] -authors = ['Parity Technologies '] -edition = '2018' -name = 'node-template-runtime-wasm' -version = '1.0.0' diff --git a/kitchen/runtimes/supernode/runtime/wasm/build.sh b/kitchen/runtimes/supernode/runtime/wasm/build.sh deleted file mode 100755 index f5761cbb1..000000000 --- a/kitchen/runtimes/supernode/runtime/wasm/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -e - -if cargo --version | grep -q "nightly"; then - CARGO_CMD="cargo" -else - CARGO_CMD="cargo +nightly" -fi -CARGO_INCREMENTAL=0 RUSTFLAGS="-C link-arg=--export-table" $CARGO_CMD build --target=wasm32-unknown-unknown --release -for i in node_template_runtime_wasm -do - wasm-gc target/wasm32-unknown-unknown/release/$i.wasm target/wasm32-unknown-unknown/release/$i.compact.wasm -done diff --git a/kitchen/runtimes/supernode/runtime/wasm/src/lib.rs b/kitchen/runtimes/supernode/runtime/wasm/src/lib.rs deleted file mode 100644 index 224f85481..000000000 --- a/kitchen/runtimes/supernode/runtime/wasm/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! The Substrate node template runtime reexported for WebAssembly compile. - -#![cfg_attr(not(feature = "std"), no_std)] - -pub use node_template_runtime::*; diff --git a/kitchen/runtimes/supernode/scripts/build.sh b/kitchen/runtimes/supernode/scripts/build.sh deleted file mode 100755 index 01d0fee35..000000000 --- a/kitchen/runtimes/supernode/scripts/build.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -set -e - -PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." >/dev/null && pwd )" - -export CARGO_INCREMENTAL=0 - -bold=$(tput bold) -normal=$(tput sgr0) - -# Save current directory. -pushd . >/dev/null - -for SRC in runtime/wasm -do - echo "${bold}Building webassembly binary in $SRC...${normal}" - cd "$PROJECT_ROOT/$SRC" - - ./build.sh - - cd - >> /dev/null -done - -# Restore initial directory. -popd >/dev/null diff --git a/kitchen/runtimes/supernode/scripts/init.sh b/kitchen/runtimes/supernode/scripts/init.sh deleted file mode 100755 index cf5ecf979..000000000 --- a/kitchen/runtimes/supernode/scripts/init.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -e - -echo "*** Initializing WASM build environment" - -if [ -z $CI_PROJECT_NAME ] ; then - rustup update nightly - rustup update stable -fi - -rustup target add wasm32-unknown-unknown --toolchain nightly - -# Install wasm-gc. It's useful for stripping slimming down wasm binaries. -command -v wasm-gc || \ - cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force diff --git a/kitchen/runtimes/supernode/src/chain_spec.rs b/kitchen/runtimes/supernode/src/chain_spec.rs deleted file mode 100644 index 3cb8d21d5..000000000 --- a/kitchen/runtimes/supernode/src/chain_spec.rs +++ /dev/null @@ -1,119 +0,0 @@ -use primitives::{ed25519, sr25519, Pair}; -use node_template_runtime::{ - AccountId, GenesisConfig, ConsensusConfig, TimestampConfig, BalancesConfig, - SudoConfig, IndicesConfig, -}; -use substrate_service; - -use ed25519::Public as AuthorityId; - -// Note this is the URL for the telemetry server -//const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; - -/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. -pub type ChainSpec = substrate_service::ChainSpec; - -/// The chain specification option. This is expected to come in from the CLI and -/// is little more than one of a number of alternatives which can easily be converted -/// from a string (`--chain=...`) into a `ChainSpec`. -#[derive(Clone, Debug)] -pub enum Alternative { - /// Whatever the current runtime is, with just Alice as an auth. - Development, - /// Whatever the current runtime is, with simple Alice/Bob auths. - LocalTestnet, -} - -fn authority_key(s: &str) -> AuthorityId { - ed25519::Pair::from_string(&format!("//{}", s), None) - .expect("static values are valid; qed") - .public() -} - -fn account_key(s: &str) -> AccountId { - sr25519::Pair::from_string(&format!("//{}", s), None) - .expect("static values are valid; qed") - .public() -} - -impl Alternative { - /// Get an actual chain config from one of the alternatives. - pub(crate) fn load(self) -> Result { - Ok(match self { - Alternative::Development => ChainSpec::from_genesis( - "Development", - "dev", - || testnet_genesis(vec![ - authority_key("Alice") - ], vec![ - account_key("Alice") - ], - account_key("Alice") - ), - vec![], - None, - None, - None, - None - ), - Alternative::LocalTestnet => ChainSpec::from_genesis( - "Local Testnet", - "local_testnet", - || testnet_genesis(vec![ - authority_key("Alice"), - authority_key("Bob"), - ], vec![ - account_key("Alice"), - account_key("Bob"), - account_key("Charlie"), - account_key("Dave"), - account_key("Eve"), - account_key("Ferdie"), - ], - account_key("Alice"), - ), - vec![], - None, - None, - None, - None - ), - }) - } - - pub(crate) fn from(s: &str) -> Option { - match s { - "dev" => Some(Alternative::Development), - "" | "local" => Some(Alternative::LocalTestnet), - _ => None, - } - } -} - -fn testnet_genesis(initial_authorities: Vec, endowed_accounts: Vec, root_key: AccountId) -> GenesisConfig { - GenesisConfig { - consensus: Some(ConsensusConfig { - code: include_bytes!("../runtime/wasm/target/wasm32-unknown-unknown/release/node_template_runtime_wasm.compact.wasm").to_vec(), - authorities: initial_authorities.clone(), - }), - system: None, - timestamp: Some(TimestampConfig { - minimum_period: 5, // 10 second block time. - }), - indices: Some(IndicesConfig { - ids: endowed_accounts.clone(), - }), - balances: Some(BalancesConfig { - transaction_base_fee: 1, - transaction_byte_fee: 0, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, - balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), - vesting: vec![], - }), - sudo: Some(SudoConfig { - key: root_key, - }), - } -} diff --git a/kitchen/runtimes/supernode/src/cli.rs b/kitchen/runtimes/supernode/src/cli.rs deleted file mode 100644 index 258d2194a..000000000 --- a/kitchen/runtimes/supernode/src/cli.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::service; -use futures::{future, Future, sync::oneshot}; -use std::cell::RefCell; -use tokio::runtime::Runtime; -pub use substrate_cli::{VersionInfo, IntoExit, error}; -use substrate_cli::{informant, parse_and_execute, NoCustom}; -use substrate_service::{ServiceFactory, Roles as ServiceRoles}; -use crate::chain_spec; -use std::ops::Deref; -use log::info; - -/// Parse command line arguments into service configuration. -pub fn run(args: I, exit: E, version: VersionInfo) -> error::Result<()> where - I: IntoIterator, - T: Into + Clone, - E: IntoExit, -{ - parse_and_execute::( - load_spec, &version, "substrate-node", args, exit, - |exit, _custom_args, config| { - info!("{}", version.name); - info!(" version {}", config.full_version()); - info!(" by {}, 2017, 2018", version.author); - info!("Chain specification: {}", config.chain_spec.name()); - info!("Node name: {}", config.name); - info!("Roles: {:?}", config.roles); - let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?; - let executor = runtime.executor(); - match config.roles { - ServiceRoles::LIGHT => run_until_exit( - runtime, - service::Factory::new_light(config, executor).map_err(|e| format!("{:?}", e))?, - exit - ), - _ => run_until_exit( - runtime, - service::Factory::new_full(config, executor).map_err(|e| format!("{:?}", e))?, - exit - ), - }.map_err(|e| format!("{:?}", e)) - } - ).map_err(Into::into).map(|_| ()) -} - -fn load_spec(id: &str) -> Result, String> { - Ok(match chain_spec::Alternative::from(id) { - Some(spec) => Some(spec.load()?), - None => None, - }) -} - -fn run_until_exit( - mut runtime: Runtime, - service: T, - e: E, -) -> error::Result<()> - where - T: Deref>, - C: substrate_service::Components, - E: IntoExit, -{ - let (exit_send, exit) = exit_future::signal(); - - let executor = runtime.executor(); - informant::start(&service, exit.clone(), executor.clone()); - - let _ = runtime.block_on(e.into_exit()); - exit_send.fire(); - - // we eagerly drop the service so that the internal exit future is fired, - // but we need to keep holding a reference to the global telemetry guard - let _telemetry = service.telemetry(); - drop(service); - Ok(()) -} - -// handles ctrl-c -pub struct Exit; -impl IntoExit for Exit { - type Exit = future::MapErr, fn(oneshot::Canceled) -> ()>; - fn into_exit(self) -> Self::Exit { - // can't use signal directly here because CtrlC takes only `Fn`. - let (exit_send, exit) = oneshot::channel(); - - let exit_send_cell = RefCell::new(Some(exit_send)); - ctrlc::set_handler(move || { - if let Some(exit_send) = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take() { - exit_send.send(()).expect("Error sending exit notification"); - } - }).expect("Error setting Ctrl-C handler"); - - exit.map_err(drop) - } -} diff --git a/kitchen/runtimes/supernode/src/error.rs b/kitchen/runtimes/supernode/src/error.rs deleted file mode 100644 index a8aa94bf3..000000000 --- a/kitchen/runtimes/supernode/src/error.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Initialization errors. - -use client; - -error_chain! { - foreign_links { - Io(::std::io::Error) #[doc="IO error"]; - Cli(::clap::Error) #[doc="CLI error"]; - } - links { - Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; - } -} diff --git a/kitchen/runtimes/supernode/src/main.rs b/kitchen/runtimes/supernode/src/main.rs deleted file mode 100644 index 53845ddd0..000000000 --- a/kitchen/runtimes/supernode/src/main.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Substrate Node Template CLI library. - -#![warn(missing_docs)] -#![warn(unused_extern_crates)] - -mod chain_spec; -mod service; -mod cli; - -pub use substrate_cli::{VersionInfo, IntoExit, error}; - -fn run() -> cli::error::Result<()> { - let version = VersionInfo { - name: "Substrate Node", - commit: env!("VERGEN_SHA_SHORT"), - version: env!("CARGO_PKG_VERSION"), - executable_name: "node-template", - author: "Anonymous", - description: "Template Node", - support_url: "support.anonymous.an", - }; - cli::run(::std::env::args(), cli::Exit, version) -} - -error_chain::quick_main!(run); diff --git a/kitchen/runtimes/supernode/src/service.rs b/kitchen/runtimes/supernode/src/service.rs deleted file mode 100644 index 239f02f33..000000000 --- a/kitchen/runtimes/supernode/src/service.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! Service and ServiceFactory implementation. Specialized wrapper over Substrate service. - -#![warn(unused_extern_crates)] - -use std::sync::Arc; -use log::info; -use transaction_pool::{self, txpool::{Pool as TransactionPool}}; -use node_template_runtime::{self, GenesisConfig, opaque::Block, RuntimeApi}; -use substrate_service::{ - FactoryFullConfiguration, LightComponents, FullComponents, FullBackend, - FullClient, LightClient, LightBackend, FullExecutor, LightExecutor, - TaskExecutor, -}; -use basic_authorship::ProposerFactory; -use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration, NothingExtra}; -use substrate_client as client; -use primitives::{ed25519::Pair, Pair as PairT}; -use inherents::InherentDataProviders; -use network::construct_simple_protocol; -use substrate_executor::native_executor_instance; -use substrate_service::construct_service_factory; - -pub use substrate_executor::NativeExecutor; -// Our native executor instance. -native_executor_instance!( - pub Executor, - node_template_runtime::api::dispatch, - node_template_runtime::native_version, - include_bytes!("../runtime/wasm/target/wasm32-unknown-unknown/release/node_template_runtime_wasm.compact.wasm") -); - -#[derive(Default)] -pub struct NodeConfig { - inherent_data_providers: InherentDataProviders, -} - -construct_simple_protocol! { - /// Demo protocol attachment for substrate. - pub struct NodeProtocol where Block = Block { } -} - -construct_service_factory! { - struct Factory { - Block = Block, - RuntimeApi = RuntimeApi, - NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, - RuntimeDispatch = Executor, - FullTransactionPoolApi = transaction_pool::ChainApi, FullExecutor, Block, RuntimeApi>, Block> - { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) }, - LightTransactionPoolApi = transaction_pool::ChainApi, LightExecutor, Block, RuntimeApi>, Block> - { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) }, - Genesis = GenesisConfig, - Configuration = NodeConfig, - FullService = FullComponents - { |config: FactoryFullConfiguration, executor: TaskExecutor| - FullComponents::::new(config, executor) - }, - AuthoritySetup = { - |service: Self::FullService, executor: TaskExecutor, key: Option>| { - if let Some(key) = key { - info!("Using authority key {}", key.public()); - let proposer = Arc::new(ProposerFactory { - client: service.client(), - transaction_pool: service.transaction_pool(), - inherents_pool: service.inherents_pool(), - }); - let client = service.client(); - executor.spawn(start_aura( - SlotDuration::get_or_compute(&*client)?, - key.clone(), - client.clone(), - client, - proposer, - service.network(), - service.on_exit(), - service.config.custom.inherent_data_providers.clone(), - service.config.force_authoring, - )?); - } - - Ok(service) - } - }, - LightService = LightComponents - { |config, executor| >::new(config, executor) }, - FullImportQueue = AuraImportQueue< - Self::Block, - > - { |config: &mut FactoryFullConfiguration , client: Arc>| { - import_queue::<_, _, _, Pair>( - SlotDuration::get_or_compute(&*client)?, - client.clone(), - None, - client, - NothingExtra, - config.custom.inherent_data_providers.clone(), - ).map_err(Into::into) - } - }, - LightImportQueue = AuraImportQueue< - Self::Block, - > - { |config: &mut FactoryFullConfiguration, client: Arc>| { - import_queue::<_, _, _, Pair>( - SlotDuration::get_or_compute(&*client)?, - client.clone(), - None, - client, - NothingExtra, - config.custom.inherent_data_providers.clone(), - ).map_err(Into::into) - } - }, - } -} From fda1633e78db047af3cb670452fb6ce74265c4b4 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Thu, 26 Sep 2019 13:57:02 +0200 Subject: [PATCH 12/23] append vs mutate for vec storage, linked map recipe --- kitchen/modules/append-to-vec/Cargo.toml | 56 +++++++++++++++++++ kitchen/modules/append-to-vec/src/lib.rs | 54 ++++++++++++++++++ kitchen/modules/child-trie/Cargo.toml | 0 kitchen/modules/custom-origin/Cargo.toml | 0 kitchen/modules/double-map/Cargo.toml | 0 kitchen/modules/double-map/src/lib.rs | 0 .../modules/{list => linked-map}/Cargo.toml | 0 .../modules/{list => linked-map}/src/lib.rs | 50 +++++++++-------- kitchen/modules/simple-map/src/lib.rs | 2 - kitchen/runtimes/README.md | 8 ++- 10 files changed, 145 insertions(+), 25 deletions(-) create mode 100644 kitchen/modules/append-to-vec/Cargo.toml create mode 100644 kitchen/modules/append-to-vec/src/lib.rs create mode 100644 kitchen/modules/child-trie/Cargo.toml create mode 100644 kitchen/modules/custom-origin/Cargo.toml create mode 100644 kitchen/modules/double-map/Cargo.toml create mode 100644 kitchen/modules/double-map/src/lib.rs rename kitchen/modules/{list => linked-map}/Cargo.toml (100%) rename kitchen/modules/{list => linked-map}/src/lib.rs (70%) diff --git a/kitchen/modules/append-to-vec/Cargo.toml b/kitchen/modules/append-to-vec/Cargo.toml new file mode 100644 index 000000000..a506f6979 --- /dev/null +++ b/kitchen/modules/append-to-vec/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "append-to-vec" +version = "0.1.0" +authors = ["4meta5"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'parity-scale-codec/std', + 'support/std', + 'system/std', + 'balances/std', + 'runtime-primitives/std', +] + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.5' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.runtime-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dev-dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' + +[dev-dependencies.runtime-io] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-io' +branch = 'master' diff --git a/kitchen/modules/append-to-vec/src/lib.rs b/kitchen/modules/append-to-vec/src/lib.rs new file mode 100644 index 000000000..713b0696e --- /dev/null +++ b/kitchen/modules/append-to-vec/src/lib.rs @@ -0,0 +1,54 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +// demonstrates how to use append instead of mutate +// https://crates.parity.io/srml_support/storage/trait.StorageValue.html#tymethod.append +use support::{decl_module, decl_storage, decl_event, StorageValue, dispatch::Result}; +use system::ensure_signed; + +pub trait Trait: system::Trait { + type Event: From> + Into<::Event>; +} + +decl_storage! { + trait Store for Module as VecMap { + CurrentValues get(current_values): Vec; + NewValues get(new_values): Vec; + } +} + +decl_event!( + pub enum Event where AccountId = ::AccountId { + // mutate to append + MutateToAppend(AccountId), + // append + AppendVec(AccountId), + } +); + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + // don't do this + // (unless appending new entries AND mutating existing entries) + fn mutate_to_append(origin) -> Result { + let user = ensure_signed(origin)?; + + // this decodes the existing vec, appends the new values, and re-encodes the whole thing + ::mutate(|v| v.extend_from_slice(&Self::new_values())); + Self::deposit_event(RawEvent::MutateToAppend(user)); + Ok(()) + } + + // do this instead + fn append_new_entries(origin) -> Result { + let user = ensure_signed(origin)?; + + // this encodes the new values and appends them to the already encoded existing evc + let mut current_values = Self::current_values(); + current_values.append(&mut Self::new_values()); + Self::deposit_event(RawEvent::AppendVec(user)); + Ok(()) + } // more examples in srml/elections-phragmen + } +}// todo: append_or_insert, append_or_put \ No newline at end of file diff --git a/kitchen/modules/child-trie/Cargo.toml b/kitchen/modules/child-trie/Cargo.toml new file mode 100644 index 000000000..e69de29bb diff --git a/kitchen/modules/custom-origin/Cargo.toml b/kitchen/modules/custom-origin/Cargo.toml new file mode 100644 index 000000000..e69de29bb diff --git a/kitchen/modules/double-map/Cargo.toml b/kitchen/modules/double-map/Cargo.toml new file mode 100644 index 000000000..e69de29bb diff --git a/kitchen/modules/double-map/src/lib.rs b/kitchen/modules/double-map/src/lib.rs new file mode 100644 index 000000000..e69de29bb diff --git a/kitchen/modules/list/Cargo.toml b/kitchen/modules/linked-map/Cargo.toml similarity index 100% rename from kitchen/modules/list/Cargo.toml rename to kitchen/modules/linked-map/Cargo.toml diff --git a/kitchen/modules/list/src/lib.rs b/kitchen/modules/linked-map/src/lib.rs similarity index 70% rename from kitchen/modules/list/src/lib.rs rename to kitchen/modules/linked-map/src/lib.rs index e0cfb796c..e5a623341 100644 --- a/kitchen/modules/list/src/lib.rs +++ b/kitchen/modules/linked-map/src/lib.rs @@ -7,7 +7,7 @@ /// fees ([Big O notation refresher](https://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/)). /// This opens an economic attack vector on your chain. -use support::{ensure, decl_module, decl_storage, decl_event, StorageValue, StorageMap, EnumerableStorageMap, dispatch::Result}; +use support::{ensure, decl_module, decl_storage, decl_event, StorageValue, StorageMap, StorageLinkedMap, dispatch::Result}; use system::ensure_signed; pub trait Trait: system::Trait { @@ -15,7 +15,7 @@ pub trait Trait: system::Trait { } decl_storage! { - trait Store for Module as Example { + trait Store for Module as List { TheList get(the_list): map u32 => T::AccountId; TheCounter get(the_counter): u32; @@ -36,7 +36,7 @@ decl_event!( decl_module! { pub struct Module for enum Call where origin: T::Origin { // initialize the default event for this module - fn deposit_event() = default; + fn deposit_event() = default; fn add_member(origin) -> Result { let who = ensure_signed(origin)?; @@ -45,29 +45,44 @@ decl_module! { ::mutate(|count| *count + 1); // add member at the largest_index - let largest_index = ::get(); - >::insert(largest_index, who.clone()); + let new_largest_index = ::get() + 1; + >::insert(new_largest_index, who.clone()); + // incremement counter + ::put(new_largest_index); + + // (same for linked counter) + let new_linked_largest_index = ::get() + 1; + >::insert(new_linked_largest_index, who.clone()); + // increment the counter + ::put(new_linked_largest_index); Self::deposit_event(RawEvent::MemberAdded(who)); Ok(()) } + // worst option + // -- only works if the list is *unbounded* fn remove_member_unbounded(origin, index: u32) -> Result { - let who = ensure_signed(origin)?; + let _ = ensure_signed(origin)?; // verify existence ensure!(>::exists(index), "an element doesn't exist at this index"); let removed_member = >::get(index); >::remove(index); + // assumes that we do not need to adjust the list because every add just increments counter Self::deposit_event(RawEvent::MemberRemoved(removed_member)); Ok(()) } - fn remove_member_ordered(origin, index: u32) -> Result { - let who = ensure_signed(origin)?; + // ok option + // swap and pop + // -- better than `remove_member_unbounded` + // -- this pattern becomes unwieldy fast! + fn remove_member_bounded(origin, index: u32) -> Result { + let _ = ensure_signed(origin)?; ensure!(>::exists(index), "an element doesn't exist at this index"); @@ -88,21 +103,11 @@ decl_module! { Ok(()) } - fn add_member_linked(origin) -> Result { - let who = ensure_signed(origin)?; - - // increment the counter - ::mutate(|count| *count + 1); - - // add member at the largest_index - let largest_index = ::get(); - >::insert(largest_index, who.clone()); - - Ok(()) - } - + // best option (atm) + // this uses the enumerable storage map to simplify `swap and pop` + // should be generally preferred fn remove_member_linked(origin, index: u32) -> Result { - let who = ensure_signed(origin)?; + let _ = ensure_signed(origin)?; ensure!(>::exists(index), "A member does not exist at this index"); @@ -110,6 +115,7 @@ decl_module! { let member_to_remove = >::take(index); let head_member = >::get(head_index); >::insert(index, head_member); + >::insert(head_index, member_to_remove); >::remove(head_index); Ok(()) diff --git a/kitchen/modules/simple-map/src/lib.rs b/kitchen/modules/simple-map/src/lib.rs index 6aef64be6..974d2fd3d 100644 --- a/kitchen/modules/simple-map/src/lib.rs +++ b/kitchen/modules/simple-map/src/lib.rs @@ -80,7 +80,5 @@ decl_module! { >::insert(user, new_entry); Ok(()) } - - // TODO: `append` and `append_or_insert` } } \ No newline at end of file diff --git a/kitchen/runtimes/README.md b/kitchen/runtimes/README.md index 46395f2f1..086cb1a9f 100644 --- a/kitchen/runtimes/README.md +++ b/kitchen/runtimes/README.md @@ -2,4 +2,10 @@ * super-node * loosely coupled modules -* larger examples of inter-module usage `=>` look at Basti's PR and push the limits of how many modules can be used in a runtime \ No newline at end of file +* larger examples of inter-module usage `=>` look at Basti's PR and push the limits of how many modules can be used in a runtime + +> watch Joshy work on loosely coupled modules + +* https://github.com/JoshOrndorff/Marketplace + +* https://github.com/JoshOrndorff/TicTacToe \ No newline at end of file From 77129b9a0c3db478e53678665fb1eebaa39e3986 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Thu, 26 Sep 2019 14:47:53 +0200 Subject: [PATCH 13/23] add storage cache example --- kitchen/modules/double-map/src/lib.rs | 1 + kitchen/modules/storage-cache/Cargo.toml | 56 +++++++++++++++ kitchen/modules/storage-cache/src/lib.rs | 90 ++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 kitchen/modules/storage-cache/Cargo.toml create mode 100644 kitchen/modules/storage-cache/src/lib.rs diff --git a/kitchen/modules/double-map/src/lib.rs b/kitchen/modules/double-map/src/lib.rs index e69de29bb..63ea78491 100644 --- a/kitchen/modules/double-map/src/lib.rs +++ b/kitchen/modules/double-map/src/lib.rs @@ -0,0 +1 @@ +/// Double Map API example \ No newline at end of file diff --git a/kitchen/modules/storage-cache/Cargo.toml b/kitchen/modules/storage-cache/Cargo.toml new file mode 100644 index 000000000..66a2fea68 --- /dev/null +++ b/kitchen/modules/storage-cache/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "storage-cache" +version = "0.1.0" +authors = ["4meta5"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'parity-scale-codec/std', + 'support/std', + 'system/std', + 'balances/std', + 'runtime-primitives/std', +] + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.5' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.runtime-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dev-dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' + +[dev-dependencies.runtime-io] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-io' +branch = 'master' diff --git a/kitchen/modules/storage-cache/src/lib.rs b/kitchen/modules/storage-cache/src/lib.rs new file mode 100644 index 000000000..98e38fc7c --- /dev/null +++ b/kitchen/modules/storage-cache/src/lib.rs @@ -0,0 +1,90 @@ +// storage cache example +// -- minimize calls to runtime storage + +#![cfg_attr(not(feature = "std"), no_std)] + +/// Single Value Storage +use support::{decl_module, decl_storage, dispatch::Result, StorageValue}; +use system::ensure_signed; + +pub trait Trait: system::Trait {} + +decl_storage! { + trait Store for Module as StorageCache { + CopyType get(copy_type): u32; + NotCopyType get(not_copy_type): Vec; + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + + // don't do this + fn no_cache_with_copy(origin, some_val: u32) -> Result { + let _ = ensure_signed(origin)?; + let original_call = ::get(); + let some_calculation = original_call + some_val; // doesn't check for overflow either! + // this next storage call is unnecessary and is wasteful + let unnecessary_call = ::get(); + // should've just used first_call here because u32 is copy + let another_calculation = some_calculation + unnecessary_call; + ::put(another_calculation); + Ok(()) + } + + // do this instead + fn cache_with_copy(origin, some_val: u32) -> Result { + let _ = ensure_signed(origin)?; + let original_call = ::get(); + let some_calculation = original_call + some_val; // doesn't check for overflow either! + // uses the original_call because u32 is copy + let another_calculation = some_calculation + original_call; + ::put(another_calculation); + Ok(()) + } + + // dont do this + fn no_cache_with_no_copy(origin, some_val: u32) -> Result { + let _ = ensure_signed(origin)?; + let original_call = ::get(); + // vector moved + let vec_len = original_call.len(); + // this next storage call is unnecessary and is wasteful + let unnecessary_call = ::get(); + let mut new_vec = Vec::new(); + for i in 0..vec_len { // avoid iteration in the runtime... + new_vec.push(some_val + unnecessary_call[i]); + } + ::put(new_vec); + Ok(()) + } + + // do this instead + fn cache_with_no_copy(origin, some_val: u32) -> Result { + let _ = ensure_signed(origin)?; + let original_call = ::get(); + // save the future call to runtime storage + let vec_len = original_call.len(); + let mut new_vec = Vec::new(); + for i in 0..vec_len { // avoid iteration in the runtime + // use the same call as previous + new_vec.push(some_val + original_call[i]); + } + ::put(new_vec); + Ok(()) + } + + // ---- for testing purposes ---- + fn set_value(origin, val: u32) -> Result { + let _ = ensure_signed(origin)?; + ::put(val); + Ok(()) + } + + fn set_vector(origin, vec: Vec) -> Result { + let _ = ensure_signed(origin)?; + ::put(vec); + Ok(()) + } + } +} From 0bfe5f84234034856330a366f6d9bc6cac36c9d3 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Thu, 26 Sep 2019 17:12:16 +0200 Subject: [PATCH 14/23] double-map example will not compile :( --- kitchen/modules/README.md | 2 + .../{adder => adding-machine}/Cargo.toml | 0 .../{adder => adding-machine}/src/lib.rs | 0 .../modules/{token => basic-token}/Cargo.toml | 0 .../modules/{token => basic-token}/src/lib.rs | 0 .../Cargo.toml | 0 .../src/lib.rs | 0 kitchen/modules/child-trie/src/lib.rs | 4 + .../Cargo.toml | 0 .../src/lib.rs | 0 kitchen/modules/double-map/Cargo.toml | 56 +++++++++++++ kitchen/modules/double-map/src/lib.rs | 78 ++++++++++++++++++- .../modules/{random => gen-random}/Cargo.toml | 0 .../modules/{random => gen-random}/README.md | 0 .../modules/{random => gen-random}/src/lib.rs | 0 .../Cargo.toml | 0 .../src/lib.rs | 0 kitchen/modules/linked-map/Cargo.toml | 2 +- .../Cargo.toml | 0 .../src/lib.rs | 0 .../Cargo.toml | 0 .../src/lib.rs | 0 .../Cargo.toml | 0 .../README.md | 0 .../src/lib.rs | 0 .../{loop => schedule-on-finalize}/Cargo.toml | 0 .../{loop => schedule-on-finalize}/src/lib.rs | 0 .../{value => single-value}/Cargo.toml | 0 .../{value => single-value}/src/lib.rs | 0 .../{social => social-network}/Cargo.toml | 0 .../{social => social-network}/src/lib.rs | 0 kitchen/modules/srml-panic/Cargo.toml | 0 kitchen/modules/srml-panic/src/lib.rs | 1 + .../{nstructs => struct-storage}/Cargo.toml | 0 .../{nstructs => struct-storage}/src/lib.rs | 0 35 files changed, 141 insertions(+), 2 deletions(-) rename kitchen/modules/{adder => adding-machine}/Cargo.toml (100%) rename kitchen/modules/{adder => adding-machine}/src/lib.rs (100%) rename kitchen/modules/{token => basic-token}/Cargo.toml (100%) rename kitchen/modules/{token => basic-token}/src/lib.rs (100%) rename kitchen/modules/{permissioned => check-membership}/Cargo.toml (100%) rename kitchen/modules/{permissioned => check-membership}/src/lib.rs (100%) create mode 100644 kitchen/modules/child-trie/src/lib.rs rename kitchen/modules/{imbalances => currency-imbalances}/Cargo.toml (100%) rename kitchen/modules/{imbalances => currency-imbalances}/src/lib.rs (100%) rename kitchen/modules/{random => gen-random}/Cargo.toml (100%) rename kitchen/modules/{random => gen-random}/README.md (100%) rename kitchen/modules/{random => gen-random}/src/lib.rs (100%) rename kitchen/modules/{hellosubstrate => hello-substrate}/Cargo.toml (100%) rename kitchen/modules/{hellosubstrate => hello-substrate}/src/lib.rs (100%) rename kitchen/modules/{lockable => lockable-currency}/Cargo.toml (100%) rename kitchen/modules/{lockable => lockable-currency}/src/lib.rs (100%) rename kitchen/modules/{constants => module-constant-config}/Cargo.toml (100%) rename kitchen/modules/{constants => module-constant-config}/src/lib.rs (100%) rename kitchen/modules/{reservable => reservable-currency}/Cargo.toml (100%) rename kitchen/modules/{reservable => reservable-currency}/README.md (100%) rename kitchen/modules/{reservable => reservable-currency}/src/lib.rs (100%) rename kitchen/modules/{loop => schedule-on-finalize}/Cargo.toml (100%) rename kitchen/modules/{loop => schedule-on-finalize}/src/lib.rs (100%) rename kitchen/modules/{value => single-value}/Cargo.toml (100%) rename kitchen/modules/{value => single-value}/src/lib.rs (100%) rename kitchen/modules/{social => social-network}/Cargo.toml (100%) rename kitchen/modules/{social => social-network}/src/lib.rs (100%) create mode 100644 kitchen/modules/srml-panic/Cargo.toml create mode 100644 kitchen/modules/srml-panic/src/lib.rs rename kitchen/modules/{nstructs => struct-storage}/Cargo.toml (100%) rename kitchen/modules/{nstructs => struct-storage}/src/lib.rs (100%) diff --git a/kitchen/modules/README.md b/kitchen/modules/README.md index 6078195e5..2a6eeb009 100644 --- a/kitchen/modules/README.md +++ b/kitchen/modules/README.md @@ -1,5 +1,7 @@ # Modules +- [ ] **UPDATE WITH DESCRIPTIONS AND BUILDS SHOWN** + **Event**: effectively logging, scheduling, and reacting to events defined in `decl_event` * [Adding Machine](./modules/adder/) * [Simple Event (not generic)](./modules/simple-event) diff --git a/kitchen/modules/adder/Cargo.toml b/kitchen/modules/adding-machine/Cargo.toml similarity index 100% rename from kitchen/modules/adder/Cargo.toml rename to kitchen/modules/adding-machine/Cargo.toml diff --git a/kitchen/modules/adder/src/lib.rs b/kitchen/modules/adding-machine/src/lib.rs similarity index 100% rename from kitchen/modules/adder/src/lib.rs rename to kitchen/modules/adding-machine/src/lib.rs diff --git a/kitchen/modules/token/Cargo.toml b/kitchen/modules/basic-token/Cargo.toml similarity index 100% rename from kitchen/modules/token/Cargo.toml rename to kitchen/modules/basic-token/Cargo.toml diff --git a/kitchen/modules/token/src/lib.rs b/kitchen/modules/basic-token/src/lib.rs similarity index 100% rename from kitchen/modules/token/src/lib.rs rename to kitchen/modules/basic-token/src/lib.rs diff --git a/kitchen/modules/permissioned/Cargo.toml b/kitchen/modules/check-membership/Cargo.toml similarity index 100% rename from kitchen/modules/permissioned/Cargo.toml rename to kitchen/modules/check-membership/Cargo.toml diff --git a/kitchen/modules/permissioned/src/lib.rs b/kitchen/modules/check-membership/src/lib.rs similarity index 100% rename from kitchen/modules/permissioned/src/lib.rs rename to kitchen/modules/check-membership/src/lib.rs diff --git a/kitchen/modules/child-trie/src/lib.rs b/kitchen/modules/child-trie/src/lib.rs new file mode 100644 index 000000000..0a3b629d3 --- /dev/null +++ b/kitchen/modules/child-trie/src/lib.rs @@ -0,0 +1,4 @@ +// FROM ROB: What we're doing there is storing the root of another trie under a key in the +// main trie. Since keys are typically hashed in the main trie, it's a fairly +// convenient (although not the only) way to make sure that inserting many elements +// under a single mapping does not make your typical lookup path for unrelated elements longer \ No newline at end of file diff --git a/kitchen/modules/imbalances/Cargo.toml b/kitchen/modules/currency-imbalances/Cargo.toml similarity index 100% rename from kitchen/modules/imbalances/Cargo.toml rename to kitchen/modules/currency-imbalances/Cargo.toml diff --git a/kitchen/modules/imbalances/src/lib.rs b/kitchen/modules/currency-imbalances/src/lib.rs similarity index 100% rename from kitchen/modules/imbalances/src/lib.rs rename to kitchen/modules/currency-imbalances/src/lib.rs diff --git a/kitchen/modules/double-map/Cargo.toml b/kitchen/modules/double-map/Cargo.toml index e69de29bb..b4d1c2915 100644 --- a/kitchen/modules/double-map/Cargo.toml +++ b/kitchen/modules/double-map/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "double-map" +version = "0.1.0" +authors = ["4meta5"] +edition = "2018" + +[features] +default = ['std'] +std = [ + 'parity-scale-codec/std', + 'support/std', + 'system/std', + 'balances/std', + 'runtime-primitives/std', +] + +[dependencies.parity-scale-codec] +default-features = false +features = ['derive'] +version = '1.0.5' + +[dependencies.support] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-support' +branch = 'master' + +[dependencies.system] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-system' +branch = 'master' + +[dependencies.balances] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'srml-balances' +branch = 'master' + +[dependencies.runtime-primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-primitives' +branch = 'master' + +[dev-dependencies.primitives] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'substrate-primitives' +branch = 'master' + +[dev-dependencies.runtime-io] +default_features = false +git = 'https://github.com/paritytech/substrate.git' +package = 'sr-io' +branch = 'master' diff --git a/kitchen/modules/double-map/src/lib.rs b/kitchen/modules/double-map/src/lib.rs index 63ea78491..67a94583a 100644 --- a/kitchen/modules/double-map/src/lib.rs +++ b/kitchen/modules/double-map/src/lib.rs @@ -1 +1,77 @@ -/// Double Map API example \ No newline at end of file +#![cfg_attr(not(feature = "std"), no_std)] + +// Double Map Example w/ remove_prefix +// https://crates.parity.io/srml_support/storage/trait.StorageDoubleMap.html +// > provides ability to efficiently remove all entries that have a common first key + +// by providing two keys, `double_map` allows us to categorize keys by a unique identifier +// AND a unique group identifier `=>` this allows for more clean removal of values with the same group identifier + +use support::{ + ensure, decl_module, decl_storage, decl_event, dispatch::Result, + storage::{StorageDoubleMap, StorageMap, StorageValue}, +}; +use system::ensure_signed; + +pub trait Trait: system::Trait { + type Event: From> + Into<::Event>; +} + +pub type GroupIndex = u32; + +decl_storage! { + trait Store for Module as DMap { + // member score (double map) + MemberScore: double_map GroupIndex, T::AccountId => u32; + // get group ID for member + GroupMembership get(group_membership): map T::AccountId => GroupIndex; + // for fast membership checks, see check-membership recipe for more details + AllMembers get(all_members): Vec; + } +} + +decl_event!( + pub enum Event where AccountId = ::AccountId { + // remove a single member with AccountId + RemoveSingleMember(AccountId), + // remove all members with GroupId + RemoveGroup(GroupIndex), + } +); + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + fn remove_my_score(origin) -> Result { + let member_to_remove = ensure_signed(origin)?; + ensure!(Self::is_member(&member_to_remove), "not a member, can't remove"); + + let group_id = >::get(member_to_remove.clone()); + >::remove(&group_id, &member_to_remove); + Self::deposit_event(RawEvent::RemoveSingleMember(member_to_remove)); + Ok(()) + } + + fn remove_group_score(origin, group: GroupIndex) -> Result { + let member = ensure_signed(origin)?; + + let group_id = >::get(member); + // check that the member is in the group (could be improved by requiring n-of-m member support) + ensure!(group_id == group, "member isn't in the group, can't remove it"); + + // allows us to remove all group members from MemberScore at once + >::remove_prefix(&group_id); + + Self::deposit_event(RawEvent::RemoveGroup(group_id)); + Ok(()) + } + } +} + +impl Module { + // for fast membership checks (see check-membership recipe for more details) + fn is_member(who: &T::AccountId) -> bool { + >::get().contains(who) + } +} diff --git a/kitchen/modules/random/Cargo.toml b/kitchen/modules/gen-random/Cargo.toml similarity index 100% rename from kitchen/modules/random/Cargo.toml rename to kitchen/modules/gen-random/Cargo.toml diff --git a/kitchen/modules/random/README.md b/kitchen/modules/gen-random/README.md similarity index 100% rename from kitchen/modules/random/README.md rename to kitchen/modules/gen-random/README.md diff --git a/kitchen/modules/random/src/lib.rs b/kitchen/modules/gen-random/src/lib.rs similarity index 100% rename from kitchen/modules/random/src/lib.rs rename to kitchen/modules/gen-random/src/lib.rs diff --git a/kitchen/modules/hellosubstrate/Cargo.toml b/kitchen/modules/hello-substrate/Cargo.toml similarity index 100% rename from kitchen/modules/hellosubstrate/Cargo.toml rename to kitchen/modules/hello-substrate/Cargo.toml diff --git a/kitchen/modules/hellosubstrate/src/lib.rs b/kitchen/modules/hello-substrate/src/lib.rs similarity index 100% rename from kitchen/modules/hellosubstrate/src/lib.rs rename to kitchen/modules/hello-substrate/src/lib.rs diff --git a/kitchen/modules/linked-map/Cargo.toml b/kitchen/modules/linked-map/Cargo.toml index 826a2a467..47f08f767 100644 --- a/kitchen/modules/linked-map/Cargo.toml +++ b/kitchen/modules/linked-map/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "map-list" +name = "linked-map" version = "0.1.0" authors = ["4meta5"] edition = "2018" diff --git a/kitchen/modules/lockable/Cargo.toml b/kitchen/modules/lockable-currency/Cargo.toml similarity index 100% rename from kitchen/modules/lockable/Cargo.toml rename to kitchen/modules/lockable-currency/Cargo.toml diff --git a/kitchen/modules/lockable/src/lib.rs b/kitchen/modules/lockable-currency/src/lib.rs similarity index 100% rename from kitchen/modules/lockable/src/lib.rs rename to kitchen/modules/lockable-currency/src/lib.rs diff --git a/kitchen/modules/constants/Cargo.toml b/kitchen/modules/module-constant-config/Cargo.toml similarity index 100% rename from kitchen/modules/constants/Cargo.toml rename to kitchen/modules/module-constant-config/Cargo.toml diff --git a/kitchen/modules/constants/src/lib.rs b/kitchen/modules/module-constant-config/src/lib.rs similarity index 100% rename from kitchen/modules/constants/src/lib.rs rename to kitchen/modules/module-constant-config/src/lib.rs diff --git a/kitchen/modules/reservable/Cargo.toml b/kitchen/modules/reservable-currency/Cargo.toml similarity index 100% rename from kitchen/modules/reservable/Cargo.toml rename to kitchen/modules/reservable-currency/Cargo.toml diff --git a/kitchen/modules/reservable/README.md b/kitchen/modules/reservable-currency/README.md similarity index 100% rename from kitchen/modules/reservable/README.md rename to kitchen/modules/reservable-currency/README.md diff --git a/kitchen/modules/reservable/src/lib.rs b/kitchen/modules/reservable-currency/src/lib.rs similarity index 100% rename from kitchen/modules/reservable/src/lib.rs rename to kitchen/modules/reservable-currency/src/lib.rs diff --git a/kitchen/modules/loop/Cargo.toml b/kitchen/modules/schedule-on-finalize/Cargo.toml similarity index 100% rename from kitchen/modules/loop/Cargo.toml rename to kitchen/modules/schedule-on-finalize/Cargo.toml diff --git a/kitchen/modules/loop/src/lib.rs b/kitchen/modules/schedule-on-finalize/src/lib.rs similarity index 100% rename from kitchen/modules/loop/src/lib.rs rename to kitchen/modules/schedule-on-finalize/src/lib.rs diff --git a/kitchen/modules/value/Cargo.toml b/kitchen/modules/single-value/Cargo.toml similarity index 100% rename from kitchen/modules/value/Cargo.toml rename to kitchen/modules/single-value/Cargo.toml diff --git a/kitchen/modules/value/src/lib.rs b/kitchen/modules/single-value/src/lib.rs similarity index 100% rename from kitchen/modules/value/src/lib.rs rename to kitchen/modules/single-value/src/lib.rs diff --git a/kitchen/modules/social/Cargo.toml b/kitchen/modules/social-network/Cargo.toml similarity index 100% rename from kitchen/modules/social/Cargo.toml rename to kitchen/modules/social-network/Cargo.toml diff --git a/kitchen/modules/social/src/lib.rs b/kitchen/modules/social-network/src/lib.rs similarity index 100% rename from kitchen/modules/social/src/lib.rs rename to kitchen/modules/social-network/src/lib.rs diff --git a/kitchen/modules/srml-panic/Cargo.toml b/kitchen/modules/srml-panic/Cargo.toml new file mode 100644 index 000000000..e69de29bb diff --git a/kitchen/modules/srml-panic/src/lib.rs b/kitchen/modules/srml-panic/src/lib.rs new file mode 100644 index 000000000..a01006db5 --- /dev/null +++ b/kitchen/modules/srml-panic/src/lib.rs @@ -0,0 +1 @@ +// everything you shouldn't do in a runtime module...based on #36 \ No newline at end of file diff --git a/kitchen/modules/nstructs/Cargo.toml b/kitchen/modules/struct-storage/Cargo.toml similarity index 100% rename from kitchen/modules/nstructs/Cargo.toml rename to kitchen/modules/struct-storage/Cargo.toml diff --git a/kitchen/modules/nstructs/src/lib.rs b/kitchen/modules/struct-storage/src/lib.rs similarity index 100% rename from kitchen/modules/nstructs/src/lib.rs rename to kitchen/modules/struct-storage/src/lib.rs From e2a8326f78c4e2e7afe116e280801fb12b3c7948 Mon Sep 17 00:00:00 2001 From: Joshy Orndorff Date: Fri, 27 Sep 2019 12:14:22 -0800 Subject: [PATCH 15/23] Double maps require explicit second hasher. --- kitchen/modules/double-map/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitchen/modules/double-map/src/lib.rs b/kitchen/modules/double-map/src/lib.rs index 67a94583a..290934075 100644 --- a/kitchen/modules/double-map/src/lib.rs +++ b/kitchen/modules/double-map/src/lib.rs @@ -22,7 +22,7 @@ pub type GroupIndex = u32; decl_storage! { trait Store for Module as DMap { // member score (double map) - MemberScore: double_map GroupIndex, T::AccountId => u32; + MemberScore: double_map GroupIndex, twox_128(T::AccountId) => u32; // get group ID for member GroupMembership get(group_membership): map T::AccountId => GroupIndex; // for fast membership checks, see check-membership recipe for more details From ae4566bffff6188d99db762d611aee310f922876 Mon Sep 17 00:00:00 2001 From: Joshy Orndorff Date: Fri, 27 Sep 2019 12:14:22 -0800 Subject: [PATCH 16/23] Double maps require explicit second hasher. --- kitchen/modules/double-map/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitchen/modules/double-map/src/lib.rs b/kitchen/modules/double-map/src/lib.rs index 67a94583a..290934075 100644 --- a/kitchen/modules/double-map/src/lib.rs +++ b/kitchen/modules/double-map/src/lib.rs @@ -22,7 +22,7 @@ pub type GroupIndex = u32; decl_storage! { trait Store for Module as DMap { // member score (double map) - MemberScore: double_map GroupIndex, T::AccountId => u32; + MemberScore: double_map GroupIndex, twox_128(T::AccountId) => u32; // get group ID for member GroupMembership get(group_membership): map T::AccountId => GroupIndex; // for fast membership checks, see check-membership recipe for more details From 6fe81f3887f2e3b2f8318ce5ada91a1240465955 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sat, 28 Sep 2019 15:48:07 +0200 Subject: [PATCH 17/23] more changes to structure --- CONTRIBUTING.md | 2 +- kitchen/modules/double-map/src/lib.rs | 16 ++--- src/README.md | 27 ++++++--- src/SUMMARY.md | 19 ++++-- src/TEMPLATE.md | 28 +++++++++ src/advanced/cryptography/app_specific.md | 3 + src/{design/cop.md => declarative/README.md} | 56 ++++++++++++++++-- .../safemath.md => declarative/fpamath.md} | 3 +- src/{design => declarative}/safety_readme.md | 0 src/design/README.md | 26 ++++----- src/design/{econ_cost.md => econsecurity.md} | 0 src/design/errtesting.md | 49 ++++++++++++++++ src/design/paths.md | 58 ------------------- src/design/{collateral.md => ttraits.md} | 16 +++++ .../storage_readme.md => storage/README.md} | 0 src/storage/cache.md | 2 + src/storage/childtries.md | 0 src/{basics => storage}/constants.md | 0 src/storage/double.md | 0 src/{basics/list.md => storage/enumerated.md} | 0 src/storage/iterate.md | 0 src/{design => storage}/structs.md | 0 src/testing/README.md | 4 +- src/tour/README.md | 4 +- 24 files changed, 214 insertions(+), 99 deletions(-) create mode 100644 src/TEMPLATE.md create mode 100644 src/advanced/cryptography/app_specific.md rename src/{design/cop.md => declarative/README.md} (68%) rename src/{design/safemath.md => declarative/fpamath.md} (93%) rename src/{design => declarative}/safety_readme.md (100%) rename src/design/{econ_cost.md => econsecurity.md} (100%) create mode 100644 src/design/errtesting.md delete mode 100644 src/design/paths.md rename src/design/{collateral.md => ttraits.md} (81%) rename src/{basics/storage_readme.md => storage/README.md} (100%) create mode 100644 src/storage/cache.md create mode 100644 src/storage/childtries.md rename src/{basics => storage}/constants.md (100%) create mode 100644 src/storage/double.md rename src/{basics/list.md => storage/enumerated.md} (100%) create mode 100644 src/storage/iterate.md rename src/{design => storage}/structs.md (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d8eabc77b..d70f2d3d7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,5 +52,5 @@ $ mdbook build --open * don't use "we" or "our" because it often is conducive to unnecessary language * use active voice (instead of passive voice ie "you may want to use active voice") -* frequently link to outside content ie original code, blog/tutorial references, documentation for a specific method/trait/etc +* link as often as possible to outside content and useful resources including other recipes, [documentation](https://substrate.dev/docs/en/getting-started/), [tutorials](https://substrate.dev/en/tutorials), and [code](https://github.com/substrate) * **be nice, abide by the [Rust CoC](https://www.rust-lang.org/policies/code-of-conduct)** \ No newline at end of file diff --git a/kitchen/modules/double-map/src/lib.rs b/kitchen/modules/double-map/src/lib.rs index 290934075..cdb10a413 100644 --- a/kitchen/modules/double-map/src/lib.rs +++ b/kitchen/modules/double-map/src/lib.rs @@ -2,14 +2,15 @@ // Double Map Example w/ remove_prefix // https://crates.parity.io/srml_support/storage/trait.StorageDoubleMap.html -// > provides ability to efficiently remove all entries that have a common first key - -// by providing two keys, `double_map` allows us to categorize keys by a unique identifier -// AND a unique group identifier `=>` this allows for more clean removal of values with the same group identifier +// `double_map` maps two keys to a single value. +// the first key might be a group identifier +// the second key might be a unique identifier +// `remove_prefix` enables clean removal of all values with the group identifier use support::{ - ensure, decl_module, decl_storage, decl_event, dispatch::Result, + ensure, decl_module, decl_storage, decl_event, storage::{StorageDoubleMap, StorageMap, StorageValue}, + dispatch::Result, }; use system::ensure_signed; @@ -17,12 +18,13 @@ pub trait Trait: system::Trait { type Event: From> + Into<::Event>; } -pub type GroupIndex = u32; +pub type GroupIndex = u32; // this is Encode (which is necessary for double_map) decl_storage! { - trait Store for Module as DMap { + trait Store for Module as Dmap { // member score (double map) MemberScore: double_map GroupIndex, twox_128(T::AccountId) => u32; + // get group ID for member GroupMembership get(group_membership): map T::AccountId => GroupIndex; // for fast membership checks, see check-membership recipe for more details diff --git a/src/README.md b/src/README.md index 79f81351d..eef6e9689 100644 --- a/src/README.md +++ b/src/README.md @@ -2,17 +2,28 @@ - [Introduction](./base/README.md) - [Learn Rust](./base/rust.md) -- [Set Up](./base/setup.md) +- [Installation](./base/setup.md) - [Hello Substrate](./basics/README.md) + - [Events Verify Execution](./basics/events) - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - - [Maps](./basics/list.md) - - [Configurable Constants](./basics/constants.md) -- [Design Patterns](./types/README.md) - - [Currency Types](./types/collateral.md) - - [Economic Security](./safety/README.md) - - [Condition Oriented Programming](./safety/paths.md) - - [Fixed Point Arithmetic](./safety/safemath.md) +- [Runtime Storage API](./storage/README.md) + - [Cache Multiple Calls](./storage/cache.md) + - [Set Storage and Iteration](./storage/iterate.md) + - [Lists: Maps vs Linked Maps](./storage/enumerated.md) + - [Efficent Subgroup Removal by Subkey: Double Maps](./storage/double.md) + - [Cheap Inclusion Proofs: Child Tries](./storage/childtries.md) + - [Configurable Constants](./storage/constants.md) +- [Best Practices](./design/README.md) + - [Types and Traits](./design/collateral.md) + - [Economic Security](./design/econ_cost.md) + - [Runtime Errors and Testing](./design/errtesting.md) + - [Condition Oriented Programming](./design/cop.md) + - [Fixed Point Arithmetic](./design/safemath.md) + - [Verifying Signed Messages](#verify) + - [Check for Collisions](#collide) + - [Check for Overflow/Underflow](#underflow) + - [Manage State with Enums](#enums) - [Runtime Configuration](./base/runtime.md) - [Simple Treasury](./dao/README.md) - [Token Curated Registry](./token/README.md) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 051214605..eef6e9689 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -4,15 +4,26 @@ - [Learn Rust](./base/rust.md) - [Installation](./base/setup.md) - [Hello Substrate](./basics/README.md) + - [Events Verify Execution](./basics/events) - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - - [Maps](./basics/list.md) - - [Configurable Constants](./basics/constants.md) +- [Runtime Storage API](./storage/README.md) + - [Cache Multiple Calls](./storage/cache.md) + - [Set Storage and Iteration](./storage/iterate.md) + - [Lists: Maps vs Linked Maps](./storage/enumerated.md) + - [Efficent Subgroup Removal by Subkey: Double Maps](./storage/double.md) + - [Cheap Inclusion Proofs: Child Tries](./storage/childtries.md) + - [Configurable Constants](./storage/constants.md) - [Best Practices](./design/README.md) - [Types and Traits](./design/collateral.md) - - [Economic Security](./design/econsecurity.md) - - [Declarative Syntax](./design/paths.md) + - [Economic Security](./design/econ_cost.md) + - [Runtime Errors and Testing](./design/errtesting.md) + - [Condition Oriented Programming](./design/cop.md) - [Fixed Point Arithmetic](./design/safemath.md) + - [Verifying Signed Messages](#verify) + - [Check for Collisions](#collide) + - [Check for Overflow/Underflow](#underflow) + - [Manage State with Enums](#enums) - [Runtime Configuration](./base/runtime.md) - [Simple Treasury](./dao/README.md) - [Token Curated Registry](./token/README.md) diff --git a/src/TEMPLATE.md b/src/TEMPLATE.md new file mode 100644 index 000000000..db2ec0da9 --- /dev/null +++ b/src/TEMPLATE.md @@ -0,0 +1,28 @@ +# : +> [code]`(`**``**`)`, status`[`**``**`[`, [status]`[`**``**`[` + +*Status isn't ready yet...still thinking about how much structure should be defined and where it should be documented* + +```rust +pub enum Status { + OutdatedAndWrong, + ExistsImprovements(u32), + + +} +``` + +```rust +match status { + OutdatedAndWrong => replace() && (create_issue || create_pr); + ExistsImprovements => link to +} +``` + +## (optional) motivate usage with context + +## show simple api usage + +## (optional) work through each part + +## (optional) cite other examples/references \ No newline at end of file diff --git a/src/advanced/cryptography/app_specific.md b/src/advanced/cryptography/app_specific.md new file mode 100644 index 000000000..152836291 --- /dev/null +++ b/src/advanced/cryptography/app_specific.md @@ -0,0 +1,3 @@ +# Substrate Application Crypto + +* https://crates.parity.io/substrate_application_crypto/index.html \ No newline at end of file diff --git a/src/design/cop.md b/src/declarative/README.md similarity index 68% rename from src/design/cop.md rename to src/declarative/README.md index 8d9aeded6..1160b2bbb 100644 --- a/src/design/cop.md +++ b/src/declarative/README.md @@ -1,10 +1,8 @@ -# Declarative Programming +## Declarative Programming Within each runtime module function, it is important to perform all checks prior to any storage changes. When coding on most smart contract platforms, the stakes are lower because panics on contract calls will revert any storage changes. Conversely, Substrate requires greater attention to detail because mid-function panics will persist any prior changes made to storage. * [Using the Ensure Macro](#ensure) -* [Verifying Signed Messages](#verify) -* [Checking for Collisions](#collide) ## Using the Ensure Macro @@ -86,4 +84,54 @@ fn insert_value(origin, hash: Hash, value: u32) { } ``` -*See how the [Substrate Collectables Tutorial](https://shawntabrizi.com/substrate-collectables-workshop/#/2/generating-random-data?id=checking-for-collision) covers this pattern.* \ No newline at end of file +*See how the [Substrate Collectables Tutorial](https://shawntabrizi.com/substrate-collectables-workshop/#/2/generating-random-data?id=checking-for-collision) covers this pattern.* + +## Ergonomic Enums + +In the [`utxo-workshop`](https://github.com/nczhu/utxo-workshop), the code utilizes an [enum](https://doc.rust-lang.org/rust-by-example/custom_types/enum.html) to manage a data race scenario in which a transaction could arrive before some transactions that it `require`s. + +*Alice sends Bob 100 units and Bob sends Eve 80 of those units. Let's assume that Bob's transaction is dependent upon Alice's transaction. If Alice's transaction takes a few more seconds to arrive, we do not want to throw out Bob's transaction. Instead of panicking, we should place Bob's transaction in a temporary queue and lock it for some defined time period.* + +To see this pattern in action, see the `check_transaction` runtime function: + +```rust +pub fn check_transaction(transaction: &Transaction) -> CheckResult<'_> +``` + +This function returns `CheckResult<'_>`. The type signature of `CheckResult`: + +```rust +pub type CheckResult<'a> = rstd::result::Result, &'static str>; +``` + +The type signature of `CheckInfo`: + +```rust +/// Information collected during transaction verification +pub enum CheckInfo<'a> { + /// Combined value of all inputs and outputs + Totals { input: Value, output: Value }, + + /// Some referred UTXOs were missing + MissingInputs(Vec<&'a H256>), +} +``` + +This reveals that in the event of a successful call, it returns either the `Total`s struct that can be easily decomposed to calculate leftover value and distribute it evenly among the authorities OR returns a wrapper around the missing UTXOs which were necessary for verification. Here's the code in `check_transaction` that expresses this logic: + +```rust +if missing_utxo.is_empty() { + ensure!( + total_input >= total_output, + "output value must not exceed input value" + ); + Ok(CheckInfo::Totals { + input: total_input, + output: total_input, + }) +} else { + Ok(CheckInfo::MissingInputs(missing_utxo)) +} +``` + +This pattern demonstrates one way to safely handle the common data race that occurs when a conditional transaction arrives in the transaction pool before the arrival of a transaction that it `require`s. *We can extract this pattern to safely handle conditional paths in our code for which panics are undesirable, but it is also preferrable to pause processing.* \ No newline at end of file diff --git a/src/design/safemath.md b/src/declarative/fpamath.md similarity index 93% rename from src/design/safemath.md rename to src/declarative/fpamath.md index df55245ba..df5246f06 100644 --- a/src/design/safemath.md +++ b/src/declarative/fpamath.md @@ -2,13 +2,14 @@ *computers are notoriously bad with precision with small numbers* -see https://github.com/substrate-developer-hub/recipes/issues/12 for links +* see https://github.com/substrate-developer-hub/recipes/issues/12 for links ## Perbill and Permill Usage ## Saturating Operations ## Checking for Overflows/Underflows +> **explain here how context matters for these checks We can use the `checked` traits in [substrate-primitives](https://crates.parity.io/sr_primitives/traits/index.html) to protect against [overflow/underflow](https://medium.com/@taabishm2/integer-overflow-underflow-and-floating-point-imprecision-6ba869a99033) when incrementing/decrementing objects in our runtime. To follow the [Substrat collectable tutorial example](https://shawntabrizi.com/substrate-collectables-workshop/#/2/tracking-all-kitties?id=checking-for-overflowunderflow), we use [`checked_add()`](https://crates.parity.io/sr_primitives/traits/trait.CheckedAdd.html) to safely handle the possibility of overflow when incremementing a global counter. *Note that this check is similar to [`SafeMath`](https://ethereumdev.io/safemath-protect-overflows/) in Solidity*. diff --git a/src/design/safety_readme.md b/src/declarative/safety_readme.md similarity index 100% rename from src/design/safety_readme.md rename to src/declarative/safety_readme.md diff --git a/src/design/README.md b/src/design/README.md index e25b31cfb..d69434e11 100644 --- a/src/design/README.md +++ b/src/design/README.md @@ -1,13 +1,13 @@ -# Substrate Types and Traits - -To access Substrate specific types, the module's `Trait` must inherit from the [SRML](https://github.com/paritytech/substrate/tree/master/srml). For example, to access the Substrate types `Hash`, `AccountId`, and `BlockNumber`, it is sufficient to inherit the [`system`](https://github.com/paritytech/substrate/tree/master/srml/system) module: - -```rust -pub trait Trait: system::Trait {} -``` - -This provides access to the types `Hash`, `AccountId`, and `BlockNumber` anywhere that specifies the generic `` using `T::`. It also provides access to other useful types, declared in the `pub Trait {}` block in [`systems/src/lib.rs`](https://github.com/paritytech/substrate/blob/v1.0/srml/system/src/lib.rs). - -## support::traits - -Unlike in smart contract development, the way to inherit shared behavior is not to directly import other modules. Instead, it is common to either implement the same logic in the new context or utilize a trait from [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/traits.rs) to guide the new implementation. By abstracting shared behavior from the runtime modules into [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/traits.rs), Substrate makes it easy to extract and enforce best practices in the runtime. You can find the trait documentation [here](https://crates.parity.io/srml_support/traits/index.html). *See [Using Balances](./currency.md) for an example of usage*. \ No newline at end of file +# Best Practices + +* [Types and Traits](./ttraits.md) +* [Economic Security](./econsecurity.md) +* [Condition-Oriented Programming](./cop) +* [Runtime Errors and Testing](./errtesting.md) + +*short patterns* +* [Verifying Signed Messages](#verify) +* [Checking for Collisions](#collide) +* [Checking for Overflow/Underflow](#underflow) +* [Enums for Managing State](#enums) +* [Fixed Point Arithmetic](./fpamath.md) \ No newline at end of file diff --git a/src/design/econ_cost.md b/src/design/econsecurity.md similarity index 100% rename from src/design/econ_cost.md rename to src/design/econsecurity.md diff --git a/src/design/errtesting.md b/src/design/errtesting.md new file mode 100644 index 000000000..542b9d5df --- /dev/null +++ b/src/design/errtesting.md @@ -0,0 +1,49 @@ +# Runtime Errors and Testing + +Runtime tests allow you to verify the logic in your runtime module by mocking a Substrate runtime environment. This requires an explicit implementation of all the traits declared in the module(s) for the mock runtime. So, if our module trait looked like, + +```rust +pub trait Trait: system::Trait { + type Reward = SomeRewardType; + + type ConstThing = Get; +} +``` + +Then, the implementation would require an explicit implementation of this trait in our mock runtime (similar in structure to the [substrate](https://github.com/paritytech/substrate/tree/master/node/runtime) and [polkadot](https://github.com/paritytech/polkadot/blob/master/runtime/src/lib.rs) runtime configurations). For example, + +```rust +pub type SpecificType = u32; // could be some other type + +parameter_types!{ + pub const ConstThing = 255; +} + +impl module::Trait for Runtime { + type Reward = SpecificType; + type ConstThing = ConstThing; +} +``` + +Within the context of testing, there are a few ways of building a mock runtime that offer varying levels of customizations. The easiest way is to mock runtime storage with + + +```rust + +``` + +*To learn more about [test](), see the official docs* + +What are runtime errors? + +Panic vs Error Handling + +Reminder: never panic + +Reminder: verify first, write last + +Custom Error Messages + +[`decl_error`](https://crates.parity.io/srml_support/macro.decl_error.html) + +* testing from `balances` \ No newline at end of file diff --git a/src/design/paths.md b/src/design/paths.md deleted file mode 100644 index 179aef233..000000000 --- a/src/design/paths.md +++ /dev/null @@ -1,58 +0,0 @@ -## Robust Path Handling - -* `if let some` -* `checked_sub` pattern from recent issue (link to safe-math, but put that pattern in here)...don't link, just natural flow...reference it though - -* abstract out the pattern and make it as generic as possible - -* I think this enum is also used in the context of Kian's pull request under my name... - -### Ergonomic Enums - -In the [`utxo-workshop`](https://github.com/nczhu/utxo-workshop), the code utilizes an [enum](https://doc.rust-lang.org/rust-by-example/custom_types/enum.html) to manage a data race scenario in which a transaction could arrive before some transactions that it `require`s. - -*Alice sends Bob 100 units and Bob sends Eve 80 of those units. Let's assume that Bob's transaction is dependent upon Alice's transaction. If Alice's transaction takes a few more seconds to arrive, we do not want to throw out Bob's transaction. Instead of panicking, we should place Bob's transaction in a temporary queue and lock it for some defined time period.* - -To see this pattern in action, see the `check_transaction` runtime function: - -```rust -pub fn check_transaction(transaction: &Transaction) -> CheckResult<'_> -``` - -This function returns `CheckResult<'_>`. The type signature of `CheckResult`: - -```rust -pub type CheckResult<'a> = rstd::result::Result, &'static str>; -``` - -The type signature of `CheckInfo`: - -```rust -/// Information collected during transaction verification -pub enum CheckInfo<'a> { - /// Combined value of all inputs and outputs - Totals { input: Value, output: Value }, - - /// Some referred UTXOs were missing - MissingInputs(Vec<&'a H256>), -} -``` - -This reveals that in the event of a successful call, it returns either the `Total`s struct that can be easily decomposed to calculate leftover value and distribute it evenly among the authorities OR returns a wrapper around the missing UTXOs which were necessary for verification. Here's the code in `check_transaction` that expresses this logic: - -```rust -if missing_utxo.is_empty() { - ensure!( - total_input >= total_output, - "output value must not exceed input value" - ); - Ok(CheckInfo::Totals { - input: total_input, - output: total_input, - }) -} else { - Ok(CheckInfo::MissingInputs(missing_utxo)) -} -``` - -This pattern demonstrates one way to safely handle the common data race that occurs when a conditional transaction arrives in the transaction pool before the arrival of a transaction that it `require`s. *We can extract this pattern to safely handle conditional paths in our code for which panics are undesirable, but it is also preferrable to pause processing.* \ No newline at end of file diff --git a/src/design/collateral.md b/src/design/ttraits.md similarity index 81% rename from src/design/collateral.md rename to src/design/ttraits.md index 88151a420..e29832db1 100644 --- a/src/design/collateral.md +++ b/src/design/ttraits.md @@ -1,3 +1,19 @@ +# Substrate Types and Traits + +To access Substrate specific types, the module's `Trait` must inherit from the [SRML](https://github.com/paritytech/substrate/tree/master/srml). For example, to access the Substrate types `Hash`, `AccountId`, and `BlockNumber`, it is sufficient to inherit the [`system`](https://github.com/paritytech/substrate/tree/master/srml/system) module: + +```rust +pub trait Trait: system::Trait {} +``` + +This provides access to the types `Hash`, `AccountId`, and `BlockNumber` anywhere that specifies the generic `` using `T::`. It also provides access to other useful types, declared in the `pub Trait {}` block in [`systems/src/lib.rs`](https://github.com/paritytech/substrate/blob/v1.0/srml/system/src/lib.rs). + +## support::traits + +Unlike in smart contract development, the way to inherit shared behavior is not to directly import other modules. Instead, it is common to either implement the same logic in the new context or utilize a trait from [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/traits.rs) to guide the new implementation. By abstracting shared behavior from the runtime modules into [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/traits.rs), Substrate makes it easy to extract and enforce best practices in the runtime. You can find the trait documentation [here](https://crates.parity.io/srml_support/traits/index.html). *See [Using Balances](./currency.md) for an example of usage*. + + + # Currency Types and Locking Techniques To use a balances type in the runtime, import the [`Currency`](https://crates.parity.io/srml_support/traits/trait.Currency.html) trait from `srml/support` diff --git a/src/basics/storage_readme.md b/src/storage/README.md similarity index 100% rename from src/basics/storage_readme.md rename to src/storage/README.md diff --git a/src/storage/cache.md b/src/storage/cache.md new file mode 100644 index 000000000..e50937321 --- /dev/null +++ b/src/storage/cache.md @@ -0,0 +1,2 @@ +# Cache Multiple Storage Calls + diff --git a/src/storage/childtries.md b/src/storage/childtries.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/basics/constants.md b/src/storage/constants.md similarity index 100% rename from src/basics/constants.md rename to src/storage/constants.md diff --git a/src/storage/double.md b/src/storage/double.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/basics/list.md b/src/storage/enumerated.md similarity index 100% rename from src/basics/list.md rename to src/storage/enumerated.md diff --git a/src/storage/iterate.md b/src/storage/iterate.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/design/structs.md b/src/storage/structs.md similarity index 100% rename from src/design/structs.md rename to src/storage/structs.md diff --git a/src/testing/README.md b/src/testing/README.md index 340c4fefa..7dac8f22c 100644 --- a/src/testing/README.md +++ b/src/testing/README.md @@ -1,6 +1,8 @@ # Module Testing -This section has been deprecated because it would repeat information from [Crypto Collectables Testing](https://www.shawntabrizi.com/substrate-collectables-workshop/#/5/introduction). +* this section will be used to hold the information from `best-practices/errtesting` when that section inevitably outgrows a single recipe format (which is a priority) + +* [Crypto Collectables Testing](https://www.shawntabrizi.com/substrate-collectables-workshop/#/5/introduction). Things to cover not covered in that example include diff --git a/src/tour/README.md b/src/tour/README.md index c8526c2f5..8645a5edd 100644 --- a/src/tour/README.md +++ b/src/tour/README.md @@ -1,12 +1,12 @@ # SRML Tour -(**deprecated**) +(*deprecated in favor of structure described in the README; if people voice interest in bringing back srml-tour, I'll spend some time on it*) [srml-tour](https://github.com/JoshOrndorff/srml-tour) intends to explain the features of SRML modules, demonstrate use cases, and explore the code. It is *in progress*, tracked in [issues](https://github.com/substrate-developer-hub/recipes/issues). ## smpl-treasury -**[recipe](./treasury.md)**, [kitchen/treasury](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/treasury) +**[recipe](./treasury.md)**, [kitchen/treasury](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen//modules/treasury) 1. instantiate a pot 2. proxy spending through the pot 3. schedule spending with configurable module constants \ No newline at end of file From f5ea83f9553e4ddcbec97ae73ef601fa8226d7f7 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 29 Sep 2019 17:42:46 +0200 Subject: [PATCH 18/23] add storage cache recipe writing --- CONTRIBUTING.md | 1 + kitchen/modules/generic-event/src/lib.rs | 4 +- .../modules/schedule-on-finalize/src/lib.rs | 2 +- kitchen/modules/simple-event/src/lib.rs | 2 +- kitchen/modules/single-value/Cargo.toml | 8 +- kitchen/modules/single-value/src/lib.rs | 59 ++++++- kitchen/modules/social-network/Cargo.toml | 2 +- kitchen/modules/social-network/src/lib.rs | 6 +- kitchen/modules/srml-panic/src/lib.rs | 2 +- kitchen/modules/storage-cache/src/lib.rs | 147 ++++++++++++------ kitchen/modules/struct-storage/src/lib.rs | 3 +- src/README.md | 14 +- src/SUMMARY.md | 14 +- src/TEMPLATE.md | 24 ++- src/base/rust.md | 8 +- src/base/template.md | 5 - src/basics/README.md | 9 +- src/basics/adder.md | 14 +- src/basics/event_readme.md | 14 -- src/basics/events.md | 52 +++++++ src/basics/value.md | 99 ++++-------- src/declarative/collide.md | 0 src/declarative/enums.md | 0 src/declarative/overunder.md | 0 src/declarative/verify.md | 0 src/storage/README.md | 19 +-- src/storage/cache.md | 139 ++++++++++++++++- src/testing/README.md | 1 - 28 files changed, 436 insertions(+), 212 deletions(-) delete mode 100644 src/base/template.md delete mode 100644 src/basics/event_readme.md create mode 100644 src/basics/events.md create mode 100644 src/declarative/collide.md create mode 100644 src/declarative/enums.md create mode 100644 src/declarative/overunder.md create mode 100644 src/declarative/verify.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d70f2d3d7..74af2e6b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,7 @@ The **purpose** of [Substrate Recipes](https://substrate.dev/recipes/) is to identify best practices in Substrate runtime code and extract useful patterns. +* [Template](./src/TEMPLATE.md) * [Scope and Structure](#scope) * [Getting Involved](#involve) * [Mdbook Local Build Instructions](#instructions) diff --git a/kitchen/modules/generic-event/src/lib.rs b/kitchen/modules/generic-event/src/lib.rs index 13a6b304a..cd54224de 100644 --- a/kitchen/modules/generic-event/src/lib.rs +++ b/kitchen/modules/generic-event/src/lib.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -/// Event generic over multiple types (u32, AccountId) +/// Event uses types from the module trait use support::{decl_event, decl_module, dispatch::Result}; use system::ensure_signed; @@ -24,7 +24,7 @@ decl_module! { } } -// AccountId, u32 both are inputs `=>` generic event declaration +// AccountId, u32 both are inputs `=>` declaration with `` decl_event!( pub enum Event where AccountId = ::AccountId { EmitInput(AccountId, u32), diff --git a/kitchen/modules/schedule-on-finalize/src/lib.rs b/kitchen/modules/schedule-on-finalize/src/lib.rs index 8ae75c486..9dec5815c 100644 --- a/kitchen/modules/schedule-on-finalize/src/lib.rs +++ b/kitchen/modules/schedule-on-finalize/src/lib.rs @@ -55,7 +55,7 @@ decl_storage! { decl_module! { pub struct Module for enum Call where origin: T::Origin { - fn deposit_event() = default; + fn deposit_event() = default; // frequency with which the ActionQ is executed const ExecutionFrequency: T::BlockNumber = T::ExecutionFrequency::get(); diff --git a/kitchen/modules/simple-event/src/lib.rs b/kitchen/modules/simple-event/src/lib.rs index f12ab2317..37090fac7 100644 --- a/kitchen/modules/simple-event/src/lib.rs +++ b/kitchen/modules/simple-event/src/lib.rs @@ -25,7 +25,7 @@ decl_module! { } } -// only uses u32 so does not need to be generic +// uses u32 and not types from Trait so does not require `` decl_event!( pub enum Event { EmitInput(u32), diff --git a/kitchen/modules/single-value/Cargo.toml b/kitchen/modules/single-value/Cargo.toml index 1404aa538..f0848dd58 100644 --- a/kitchen/modules/single-value/Cargo.toml +++ b/kitchen/modules/single-value/Cargo.toml @@ -17,7 +17,7 @@ std = [ [dependencies.parity-scale-codec] default-features = false features = ['derive'] -version = '1.0.5' +version = '1.0.6' [dependencies.support] default_features = false @@ -43,6 +43,12 @@ git = 'https://github.com/paritytech/substrate.git' package = 'sr-primitives' branch = 'master' +# [dependencies.rstd] +# default-features = false +# git = 'https://github.com/paritytech/substrate.git' +# package = "sr-std" +# branch = 'master' + [dev-dependencies.primitives] default_features = false git = 'https://github.com/paritytech/substrate.git' diff --git a/kitchen/modules/single-value/src/lib.rs b/kitchen/modules/single-value/src/lib.rs index 420f5f9cf..142fa61d7 100644 --- a/kitchen/modules/single-value/src/lib.rs +++ b/kitchen/modules/single-value/src/lib.rs @@ -1,23 +1,72 @@ #![cfg_attr(not(feature = "std"), no_std)] /// Single Value Storage -use support::{decl_module, decl_storage, dispatch::Result, StorageValue}; -use system::ensure_signed; +use support::{decl_module, decl_event, decl_storage, ensure, dispatch::Result, StorageValue}; +use system::{self, ensure_signed}; -pub trait Trait: system::Trait {} +pub trait Trait: system::Trait { + type Event: From> + Into<::Event>; +} decl_storage! { trait Store for Module as SingleValue { MyValue: u32; + MyAccount: T::AccountId; } } +decl_event!( + pub enum Event + where + AccountId = ::AccountId, + BlockNumber = ::BlockNumber, + { + // value set at Set, BlockNumber at time + ValueSet(u32, BlockNumber), + // requested public value get + ValueGet(u32, BlockNumber), + // requested public account get + AccountGet(AccountId, BlockNumber), + // account set at Set, BlockNumber at time + AccountSet(AccountId, BlockNumber), + } +); + decl_module! { pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + fn set_value(origin, value: u32) -> Result { - // check sender signature to verify permissions let _ = ensure_signed(origin)?; - ::put(value); + let now = >::block_number(); + ::put(value.clone()); + Self::deposit_event(RawEvent::ValueSet(value, now)); + Ok(()) + } + + fn get_value(origin) -> Result { + let _ = ensure_signed(origin)?; + let now = >::block_number(); + ensure!(::exists(), "value does not exist"); + let val = ::get(); + Self::deposit_event(RawEvent::ValueGet(val, now)); + Ok(()) + } + + fn set_account(origin, account_to_set: T::AccountId) -> Result { + let _ = ensure_signed(origin)?; + let now = >::block_number(); + >::put(account_to_set.clone()); + Self::deposit_event(RawEvent::AccountSet(account_to_set, now)); + Ok(()) + } + + fn get_account(origin) -> Result { + let _ = ensure_signed(origin)?; + let now = >::block_number(); + ensure!(>::exists(), "account dne"); + let got_account = >::get(); + Self::deposit_event(RawEvent::AccountGet(got_account, now)); Ok(()) } } diff --git a/kitchen/modules/social-network/Cargo.toml b/kitchen/modules/social-network/Cargo.toml index 367b8e1e9..c989e90d6 100644 --- a/kitchen/modules/social-network/Cargo.toml +++ b/kitchen/modules/social-network/Cargo.toml @@ -17,7 +17,7 @@ std = [ [dependencies.parity-scale-codec] default-features = false features = ['derive'] -version = '1.0.5' +version = '1.0.6' [dependencies.support] default_features = false diff --git a/kitchen/modules/social-network/src/lib.rs b/kitchen/modules/social-network/src/lib.rs index 5d35b2796..1a73550a5 100644 --- a/kitchen/modules/social-network/src/lib.rs +++ b/kitchen/modules/social-network/src/lib.rs @@ -1,8 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -/// TODO: -/// this is a really poor implementation -/// brainstorm and rewrite `=>` create a profile struct for managing friend information +/// TODO:rewrite `=>` create a profile struct for managing friend information /// use constant getters and softmax? use support::{ decl_event, decl_module, decl_storage, dispatch::Result, ensure, EnumerableStorageMap, @@ -37,7 +35,7 @@ decl_event!( decl_module! { pub struct Module for enum Call where origin: T::Origin { - fn deposit_event() = default; + fn deposit_event() = default; pub fn add_friend(origin, new_friend: T::AccountId) { let user = ensure_signed(origin)?; diff --git a/kitchen/modules/srml-panic/src/lib.rs b/kitchen/modules/srml-panic/src/lib.rs index a01006db5..16c8aeb28 100644 --- a/kitchen/modules/srml-panic/src/lib.rs +++ b/kitchen/modules/srml-panic/src/lib.rs @@ -1 +1 @@ -// everything you shouldn't do in a runtime module...based on #36 \ No newline at end of file +// everything you shouldn't do in a runtime module #36 \ No newline at end of file diff --git a/kitchen/modules/storage-cache/src/lib.rs b/kitchen/modules/storage-cache/src/lib.rs index 98e38fc7c..944976e13 100644 --- a/kitchen/modules/storage-cache/src/lib.rs +++ b/kitchen/modules/storage-cache/src/lib.rs @@ -1,90 +1,149 @@ // storage cache example -// -- minimize calls to runtime storage +// -- generally...minimize calls to runtime storage #![cfg_attr(not(feature = "std"), no_std)] /// Single Value Storage -use support::{decl_module, decl_storage, dispatch::Result, StorageValue}; +use support::{decl_module, decl_event, ensure, decl_storage, dispatch::Result, StorageValue}; use system::ensure_signed; -pub trait Trait: system::Trait {} +pub trait Trait: system::Trait { + type Event: From> + Into<::Event>; +} decl_storage! { trait Store for Module as StorageCache { - CopyType get(copy_type): u32; - NotCopyType get(not_copy_type): Vec; + // copy type + SomeCopyValue get(some_copy_value): u32; + + // clone type + KingMember get(king_member): T::AccountId; + GroupMembers get(group_members): Vec; } } +decl_event!( + pub enum Event + where + AccountId = ::AccountId, + BlockNumber = ::BlockNumber, + { + // swap old value with new value (new_value, time_now) + InefficientValueChange(u32, BlockNumber), + // '' (new_value, time_now) + BetterValueChange(u32, BlockNumber), + // swap old king with new king (old, new) + InefficientKingSwap(AccountId, AccountId), + // '' (old, new) + BetterKingSwap(AccountId, AccountId), + } +); + decl_module! { pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; - // don't do this - fn no_cache_with_copy(origin, some_val: u32) -> Result { + // (Copy) inefficient value change implementation + fn swap_value_no_cache(origin, some_val: u32) -> Result { let _ = ensure_signed(origin)?; - let original_call = ::get(); + let original_call = ::get(); let some_calculation = original_call + some_val; // doesn't check for overflow either! // this next storage call is unnecessary and is wasteful - let unnecessary_call = ::get(); + let unnecessary_call = ::get(); // should've just used first_call here because u32 is copy let another_calculation = some_calculation + unnecessary_call; - ::put(another_calculation); + ::put(another_calculation); + let now = >::block_number(); + Self::deposit_event(RawEvent::InefficientValueChange(another_calculation, now)); Ok(()) } - // do this instead - fn cache_with_copy(origin, some_val: u32) -> Result { + // (Copy) more efficient value change + fn swap_value_w_copy(origin, some_val: u32) -> Result { let _ = ensure_signed(origin)?; - let original_call = ::get(); + let original_call = ::get(); let some_calculation = original_call + some_val; // doesn't check for overflow either! // uses the original_call because u32 is copy let another_calculation = some_calculation + original_call; - ::put(another_calculation); + ::put(another_calculation); + let now = >::block_number(); + Self::deposit_event(RawEvent::InefficientValueChange(another_calculation, now)); Ok(()) } - // dont do this - fn no_cache_with_no_copy(origin, some_val: u32) -> Result { - let _ = ensure_signed(origin)?; - let original_call = ::get(); - // vector moved - let vec_len = original_call.len(); - // this next storage call is unnecessary and is wasteful - let unnecessary_call = ::get(); - let mut new_vec = Vec::new(); - for i in 0..vec_len { // avoid iteration in the runtime... - new_vec.push(some_val + unnecessary_call[i]); - } - ::put(new_vec); + // (Clone) inefficient implementation + // swaps the king account if + // (1) other account is member && + // (2) existing king isn't + fn swap_king_no_cache(origin) -> Result { + let new_king = ensure_signed(origin)?; + let existing_king = >::get(); + + // only places a new account if + // (1) the existing account is not a member && + // (2) the new account is a member + ensure!(!Self::is_member(existing_king), "is a member so maintains priority"); + ensure!(Self::is_member(new_king.clone()), "not a member so doesn't get priority"); + + // BAD (unnecessary) storage call + let old_king = >::get(); + // place new king + >::put(new_king.clone()); + + Self::deposit_event(RawEvent::InefficientKingSwap(old_king, new_king)); Ok(()) } - // do this instead - fn cache_with_no_copy(origin, some_val: u32) -> Result { - let _ = ensure_signed(origin)?; - let original_call = ::get(); - // save the future call to runtime storage - let vec_len = original_call.len(); - let mut new_vec = Vec::new(); - for i in 0..vec_len { // avoid iteration in the runtime - // use the same call as previous - new_vec.push(some_val + original_call[i]); - } - ::put(new_vec); + // (Clone) better implementation + // swaps the king account if + // (1) other account is member && + // (2) existing king isn't + fn swap_king_with_cache(origin) -> Result { + let new_king = ensure_signed(origin)?; + let existing_king = >::get(); + // prefer to clone previous call rather than repeat call unnecessarily + let old_king = existing_king.clone(); + + // only places a new account if + // (1) the existing account is not a member && + // (2) the new account is a member + ensure!(!Self::is_member(existing_king), "is a member so maintains priority"); + ensure!(Self::is_member(new_king.clone()), "not a member so doesn't get priority"); + + // + // place new king + >::put(new_king.clone()); + + Self::deposit_event(RawEvent::BetterKingSwap(old_king, new_king)); Ok(()) } // ---- for testing purposes ---- - fn set_value(origin, val: u32) -> Result { + fn set_copy(origin, val: u32) -> Result { let _ = ensure_signed(origin)?; - ::put(val); + ::put(val); Ok(()) } - fn set_vector(origin, vec: Vec) -> Result { - let _ = ensure_signed(origin)?; - ::put(vec); + fn set_king(origin) -> Result { + let user = ensure_signed(origin)?; + >::put(user); + Ok(()) + } + + fn mock_add_member(origin) -> Result { + let added = ensure_signed(origin)?; + // see `append-to-vec` recipe for better way of doing this + >::mutate(|v| v.extend_from_slice(&[added])); Ok(()) } } } + +impl Module { + // usually should be &T::AccountId + pub fn is_member(who: T::AccountId) -> bool { + // should usually use who: &T::AccountId, but showing caching best practices + >::get().contains(&who) + } +} diff --git a/kitchen/modules/struct-storage/src/lib.rs b/kitchen/modules/struct-storage/src/lib.rs index b0d772efa..749a9941a 100644 --- a/kitchen/modules/struct-storage/src/lib.rs +++ b/kitchen/modules/struct-storage/src/lib.rs @@ -3,9 +3,8 @@ use support::codec::{Decode, Encode}; /// Nested Structs use support::{ - decl_event, decl_module, decl_storage, dispatch::Result, ensure, StorageMap, StorageValue, + decl_module, decl_storage, dispatch::Result, StorageMap, StorageValue, }; -use system::ensure_signed; pub trait Trait: balances::Trait {} diff --git a/src/README.md b/src/README.md index eef6e9689..9e0b02625 100644 --- a/src/README.md +++ b/src/README.md @@ -4,7 +4,7 @@ - [Learn Rust](./base/rust.md) - [Installation](./base/setup.md) - [Hello Substrate](./basics/README.md) - - [Events Verify Execution](./basics/events) + - [Events Verify Execution](./basics/events.md) - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - [Runtime Storage API](./storage/README.md) @@ -18,12 +18,12 @@ - [Types and Traits](./design/collateral.md) - [Economic Security](./design/econ_cost.md) - [Runtime Errors and Testing](./design/errtesting.md) - - [Condition Oriented Programming](./design/cop.md) - - [Fixed Point Arithmetic](./design/safemath.md) - - [Verifying Signed Messages](#verify) - - [Check for Collisions](#collide) - - [Check for Overflow/Underflow](#underflow) - - [Manage State with Enums](#enums) + - [Condition Oriented Programming](./declarative/README.md) + - [Fixed Point Arithmetic](./declarative/fpamath.md) + - [Verifying Signed Messages](./declarative/verify.md) + - [Check for Collisions](./declarative/collide.md) + - [Check for Overflow/Underflow](./declarative/overunder.md) + - [Manage State with Enums](./declarative/enums.md) - [Runtime Configuration](./base/runtime.md) - [Simple Treasury](./dao/README.md) - [Token Curated Registry](./token/README.md) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index eef6e9689..f16c3b39f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -8,7 +8,7 @@ - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - [Runtime Storage API](./storage/README.md) - - [Cache Multiple Calls](./storage/cache.md) + - [Cache Multiple Storage Calls](./storage/cache.md) - [Set Storage and Iteration](./storage/iterate.md) - [Lists: Maps vs Linked Maps](./storage/enumerated.md) - [Efficent Subgroup Removal by Subkey: Double Maps](./storage/double.md) @@ -18,12 +18,12 @@ - [Types and Traits](./design/collateral.md) - [Economic Security](./design/econ_cost.md) - [Runtime Errors and Testing](./design/errtesting.md) - - [Condition Oriented Programming](./design/cop.md) - - [Fixed Point Arithmetic](./design/safemath.md) - - [Verifying Signed Messages](#verify) - - [Check for Collisions](#collide) - - [Check for Overflow/Underflow](#underflow) - - [Manage State with Enums](#enums) + - [Condition Oriented Programming](./declarative/README.md) + - [Fixed Point Arithmetic](./declarative/fpamath.md) + - [Verifying Signed Messages](./declarative/verify.md) + - [Check for Collisions](./declarative/collide.md) + - [Check for Overflow/Underflow](./declarative/overunder.md) + - [Manage State with Enums](./declarative/enums.md) - [Runtime Configuration](./base/runtime.md) - [Simple Treasury](./dao/README.md) - [Token Curated Registry](./token/README.md) diff --git a/src/TEMPLATE.md b/src/TEMPLATE.md index db2ec0da9..8767fd0bf 100644 --- a/src/TEMPLATE.md +++ b/src/TEMPLATE.md @@ -1,21 +1,19 @@ # : -> [code]`(`**``**`)`, status`[`**``**`[`, [status]`[`**``**`[` - -*Status isn't ready yet...still thinking about how much structure should be defined and where it should be documented* +*[code]`(`**``**`)`, [status]`(`**``**`)`* +*brainstorming* ```rust -pub enum Status { - OutdatedAndWrong, - ExistsImprovements(u32), - - +pub enum Status { + OutdatedAndWrong(T), + ExistsImprovements(T), + MotivatesNewSection(T), } -``` - -```rust +... match status { - OutdatedAndWrong => replace() && (create_issue || create_pr); - ExistsImprovements => link to + OutdatedAndWrong => replace() && link(create_issue || create_pr); + ExistsImprovements => link(issues || prs || tutorials || code); + MotivatesNewSection => link(issues || prs || tutorials || code); + _ => empty_label_at_top; } ``` diff --git a/src/base/rust.md b/src/base/rust.md index 802d921bf..c69e733a7 100644 --- a/src/base/rust.md +++ b/src/base/rust.md @@ -1,12 +1,16 @@ # Learn Rust -To be productive with [substrate](https://github.com/substrate), it is necessary to become proficient in Rust. Fortunately, the Rust community is known for comprehensive documentation and tutorials. The canonical resource for learning Rust is [The Rust Book](https://doc.rust-lang.org/book/index.html). [Rust by Example](https://doc.rust-lang.org/rust-by-example/index.html) is also a good reference for learning how to use popular crates within the Rust ecosystem. +To be productive with [substrate](https://github.com/substrate) requires some familiarity with Rust. Fortunately, the Rust community is known for comprehensive documentation and tutorials. The most common resource for initially learning Rust is [The Rust Book](https://doc.rust-lang.org/book/index.html). To see examples of popular crate usage patterns, [Rust by Example](https://doc.rust-lang.org/rust-by-example/index.html) is also convenient. -To become more familiar with commmon design patterns in Rust, see the following: +## API Design + +To become more familiar with commmon design patterns in Rust, the following links might be helpful: * [Official Rust API Guidelines](https://rust-lang-nursery.github.io/api-guidelines/about.html) * [Rust Unofficial Design Patterns](https://github.com/rust-unofficial/patterns) * [Elegant Library API Guidelines](https://deterministic.space/elegant-apis-in-rust.html) +## Optimizations + To optimize runtime performance, Substrate developers should make use of iterators, traits, and Rust's other "*zero cost* abstractions": * [Abstraction without overhead: traits in Rust](https://blog.rust-lang.org/2015/05/11/traits.html), [related conference talk](https://www.youtube.com/watch?v=Sn3JklPAVLk) * [Effectively Using Iterators in Rust](https://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html), *[iterate over a slice rather than a `vec!`](https://twitter.com/heinz_gies/status/1121490424739303425)* diff --git a/src/base/template.md b/src/base/template.md deleted file mode 100644 index 92084e9f8..000000000 --- a/src/base/template.md +++ /dev/null @@ -1,5 +0,0 @@ -# Module Overview - -* look for existing docs - -* cover the `support::trait` imports \ No newline at end of file diff --git a/src/basics/README.md b/src/basics/README.md index 1ebdb9db5..c2417a658 100644 --- a/src/basics/README.md +++ b/src/basics/README.md @@ -1,15 +1,12 @@ # Module Fundamentals +*[`kitchen/modules/hello-substrate`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/hello-substrate)* Clone the [substrate module template](https://github.com/shawntabrizi/substrate-module-template): ```bash git clone https://github.com/shawntabrizi/substrate-module-template ``` -For an in-depth explanation of using this template, see *[Creating a Substrate Runtime Module](https://substrate.dev/docs/en/tutorials/creating-a-runtime-module)*. - -## Hello Substrate - -Import the following from [`srml-support`](https://crates.parity.io/srml_support/index.html): +At the top of the `src/lib.rs` file, import the following from [`srml-support`](https://crates.parity.io/srml_support/index.html): ```rust use support::{decl_module, decl_event, decl_storage, StorageValue, StorageMap}; @@ -27,7 +24,7 @@ decl_storage! { } ``` -The runtime methods defined in [`decl_module`](https://crates.parity.io/srml_support/macro.decl_module.html) are used to define permissions for interacting with runtime storage. +Defined in [`decl_module`](https://crates.parity.io/srml_support/macro.decl_module.html), the runtime methods are specify acceptable interaction with runtime storage. ```rust decl_module! { diff --git a/src/basics/adder.md b/src/basics/adder.md index ed1094427..255415003 100644 --- a/src/basics/adder.md +++ b/src/basics/adder.md @@ -1,4 +1,5 @@ # Adding Machine +*[`kitchen/modules/adding-machine`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/adding-machine)* A simple adding machine checks for overflow and emits an event with the result, without using storage. In the module file, @@ -32,18 +33,7 @@ decl_event!( If the addition overflows, the method will return the `"Addition overflowed"` without emitting the event. Likewise, events are generally emitted at the bottom of method bodies as an indication of correct execution of all logic therein. -*NOTE*: The event described above only wraps `u32` values. If we want/need the `Event` type to contain multiple types, then we declare the following in `decl_module` - -```rust -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn deposit_event() = default; - ... - } -} -``` - -and also the `decl_event` would use this generic syntax +*NOTE*: The event described above only wraps `u32` values. If we want/need the `Event` type to contain multiple types from our runtime, then the `decl_event` would use the following syntax ```rust decl_event!( diff --git a/src/basics/event_readme.md b/src/basics/event_readme.md deleted file mode 100644 index 89a21c8ea..000000000 --- a/src/basics/event_readme.md +++ /dev/null @@ -1,14 +0,0 @@ -# Event - -In Substrate, [transaction](https://docs.substrate.dev/docs/glossary#section-transaction) finality does not guarantee the execution of functions dependent on the given transaction. To verify that functions have executed successfully, emit an [event](https://docs.substrate.dev/docs/glossary#section-events) at the bottom of the function body. - -> **Events** notify the off-chain world of successful state transitions - -To declare an event, use the [`decl_event`](https://crates.parity.io/srml_support/macro.decl_event.html) macro. - -* [Adding Machine Example](./adder.md) - -## More Resources - -* [`decl_event` wiki docs](https://wiki.parity.io/decl_event) -* [Substrate Collectables Tutorial: Creating Events](https://shawntabrizi.github.io/substrate-collectables-workshop/#/2/creating-an-event) \ No newline at end of file diff --git a/src/basics/events.md b/src/basics/events.md new file mode 100644 index 000000000..836cee1ae --- /dev/null +++ b/src/basics/events.md @@ -0,0 +1,52 @@ +# Event +*[`kitchen/modules/simple-event`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/simple-event)*, *[`kitchen/modules/generic-event`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/simple-event)* + +In Substrate, [transaction](https://docs.substrate.dev/docs/glossary#section-transaction) finality does not guarantee the execution of functions dependent on the given transaction. To verify that functions have executed successfully, emit an [event](https://docs.substrate.dev/docs/glossary#section-events) at the bottom of the function body. + +> **Events** notify the off-chain world of successful state transitions + +To declare an event, use the [`decl_event`](https://crates.parity.io/srml_support/macro.decl_event.html) macro. + +## Simple Event + +The [simplest example of an event](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/simple-event) uses the following syntax + +```rust +decl_event!( + pub enum Event { + EmitInput(u32), + } +); +``` + +The event is emitted at the bottom of the `do_something` function body: + +```rust +Self::deposit_event(Event::EmitInput(new_number)); +``` + +## Events with Module Types + +[Sometimes](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/generic-event) events might emit types from the module Trait. When the event uses types from the module, it is necessary to specify additional syntax + +```rust +decl_event!( + pub enum Event where AccountId = ::AccountId { + EmitInput(AccountId, u32), + } +); +``` + +The syntax for `deposit_event` now takes the `RawEvent` type because it is generic over the module Trait + +```rust +Self::deposit_event(RawEvent::EmitInput(user, new_number)); +``` + +*See the next example to use the simple event syntax in the context of verifying successful execution of an [adding machine](./adder.md)* + + \ No newline at end of file diff --git a/src/basics/value.md b/src/basics/value.md index 7f5cdde90..86df0bf82 100644 --- a/src/basics/value.md +++ b/src/basics/value.md @@ -1,56 +1,42 @@ # Single Value +*[`kitchen/modules/single-value`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/single-value)* -Substrate supports all primitive [Rust types](https://cheats.rs/) (`bool`, `u8`, `u32`, etc) as well as some [custom types specific to Substrate](https://github.com/paritytech/oo7/blob/master/packages/oo7-substrate/src/types.js) (`Hash`, `Balance`, `BlockNumber`, etc). - -* [Basic Storage](#basic) -* [Storage Interaction](#interact) -* [Getter Syntax](#get) -* [Setter Syntax](#set) -* [Substrate Specific Types](#sub) - -## Basic Storage - -Within a specific module, a single value (`u32` type) is stored in the runtime with this syntax: +Within a specific module, a single value (`u32` type) is stored in the runtime using the [`decl_storage`](https://wiki.parity.io/decl_storage) macro ```rust decl_storage! { - trait Store for Module as Example { + trait Store for Module as SingleValue { MyValue: u32; } } ``` -## Storage Interaction - -To interact with single storage values, it is necessary to import the `support::StorageValue` type. Functions used to access a `StorageValue` are defined in [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/storage/generator.rs): +To interact with single storage values, it is necessary to import the `support::StorageValue` type. Functions used to access a `StorageValue` are defined in [`srml/support`](https://crates.parity.io/srml_support/storage/trait.StorageValue.html#required-methods): ```rust /// Get the storage key. -fn key() -> &'static [u8]; +fn hashed_key() -> [u8; 16]; -/// true if the value is defined in storage. -fn exists(storage: &S) -> bool { - storage.exists(Self::key()) -} +/// true if the value exists in storage. +fn exists() -> bool; /// Load the value from the provided storage instance. -fn get(storage: &S) -> Self::Query; +fn get() -> Self::Query; -/// Take a value from storage, removing it afterwards. -fn take(storage: &S) -> Self::Query; +///Put the borrowed value at the key +fn put>(val: Arg); -/// Store a value under this key into the provided storage instance. -fn put(val: &T, storage: &S) { - storage.put(Self::key(), val) -} +/// Put an unsized and `Encode` value at the key +fn put_ref(val: &Arg) where T: AsRef; -/// Mutate this value -fn mutate R, S: Storage>(f: F, storage: &S) -> R; +/// Mutate the value at the key +n mutate R>(f: F) -> R; -/// Clear the storage value. -fn kill(storage: &S) { - storage.kill(Self::key()) -} +/// Takes the value at the key +fn take() -> G::Query; + +/// Clear the storage value +fn kill(); ``` Therefore, the syntax to "put" `Value`: @@ -65,55 +51,22 @@ and to "get" `Value`: let my_val = ::get(); ``` -Note that we do not need the type `T` because the value is only of one type `u32`. If the `T` was polymorphic over more than one type, the syntax would include `T` in call like - -```rust ->::put(178); -``` - -## Getter Syntax - -Storage values can also be declared with a `get` function to provide cleaner syntax for getting values. +Note that we do not need the type `T` because the value is only of one type `u32`. If the `T` was polymorphic over more than one type, the syntax would include `T` in call ```rust decl_storage! { trait Store for Module as Example { - MyValue get(value_getter): u32; + MyValue: u32; + MyAccount: T::AccountId; } } -``` - -The `get` parameter is optional, but, by including it, the module exposes a getter function (`fn value_getter() -> u32`). - -To "get" the `Value` with the getter function -```rust -let my_val = Self::value_getter(); ``` -## Setter Syntax - -Here is an example of a module that stores a `u32` value in runtime storage and provides a function `set_value` to set the given `u32`. This code follows [convention](https://deterministic.space/elegant-apis-in-rust.html#consistent-names) for naming setters in Rust. +The requirements for setting the `AccountId` stored in `MyAccount` can be specified in the runtime and exposed via ```rust -use srml_support::{StorageValue, dispatch::Result}; - -pub trait Trait: system::Trait {} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - fn set_value(origin, value: u32) -> Result { - // check sender signature to verify permissions - let sender = ensure_signed(origin)?; - ::put(value); - Ok(()) - } - } -} +>::put(some_account_id); +``` -decl_storage! { - trait Store for Module as Example { - MyValue: u32; - } -} -``` \ No newline at end of file +*The full example in [`kitchen/modules/single-value`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/single-value) emits events to also notify off-chain processes of when values were set and got.* diff --git a/src/declarative/collide.md b/src/declarative/collide.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/declarative/enums.md b/src/declarative/enums.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/declarative/overunder.md b/src/declarative/overunder.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/declarative/verify.md b/src/declarative/verify.md new file mode 100644 index 000000000..e69de29bb diff --git a/src/storage/README.md b/src/storage/README.md index fe50b71ae..1b17dfa6f 100644 --- a/src/storage/README.md +++ b/src/storage/README.md @@ -1,18 +1,19 @@ # Storage -Use the [`decl_storage`](https://wiki.parity.io/decl_storage) macro to define type-safe, persistent data that needs to be stored on-chain. +The [`decl_storage`](https://wiki.parity.io/decl_storage) documentation specifies how to define define type-safe, persistent data that needs to be stored on-chain. For crypto*currencies*, storage might consist of a mapping between account keys and corresponding balances. More generally, blockchains provide an interface to store and interact with data in a verifiable and globally irreversible way. In this context, data is stored in a series of snapshots, each of which may be accessed at a later point in time, but, once created, snapshots are considered irreversible. -Arbitrary data may be stored, as long as its data type is serializable in Substrate i.e. implements [`Encode`](https://docs.rs/parity-codec/3.1.0/parity_codec/trait.Encode.html) and [`Decode`](https://docs.rs/parity-codec/3.1.0/parity_codec/trait.Decode.html#foreign-impls) traits. +Arbitrary data may be stored, as long as its data type is serializable in Substrate i.e. implements [`Encode`](https://docs.rs/parity-scale-codec/1.0.6/parity_scale_codec/#encode) and [`Decode`](https://docs.rs/parity-scale-codec/1.0.6/parity_scale_codec/#decode) traits. -## Recipes -- [Single Value Storage](./value.md) -- [Lists as Maps](./list.md) -- [Configurable Module Constants](./constants.md) +The previous *[single-value storage](../basics/value.md)* showed how a single value can be stored in runtime storage. In this section, we cover +- [caching values rather than calling to storage multiple times](./cache.md) +- [storing sets, checking membership, and iteration](./iterate.md) +- [ordered lists with basic maps and linked maps](./enumerated.md) +- [efficient subgroup removal by key prefix with double maps](./storage/double.md) +- [configurable module constants](./constants.md) -### More Resources - -* [`decl_storage` wiki docs](https://wiki.parity.io/decl_storage) \ No newline at end of file +*in-progress* +* - [cheap inclusion proofs with child tries](./childtries.md) \ No newline at end of file diff --git a/src/storage/cache.md b/src/storage/cache.md index e50937321..daf94254a 100644 --- a/src/storage/cache.md +++ b/src/storage/cache.md @@ -1,2 +1,139 @@ -# Cache Multiple Storage Calls +# Cache Multiple Calls +*[`kitchen/modules/storage-cache`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/storage-cache)* +Calls to runtime storage have an associated cost. With this in mind, multiple calls to storage values should be avoided when possible. + +```rust +decl_storage! { + trait Store for Module as StorageCache { + // copy type + SomeCopyValue get(some_copy_value): u32; + + // clone type + KingMember get(king_member): T::AccountId; + GroupMembers get(group_members): Vec; + } +} +``` + + +For [`Copy`](https://doc.rust-lang.org/std/marker/trait.Copy.html) types, it is easy to reuse previous storage calls by simply reusing the value (which is automatically cloned upon reuse). With this in mind, the second call in the following code is unnecessary: + +```rust +fn swap_value_no_cache(origin, some_val: u32) -> Result { + let _ = ensure_signed(origin)?; + let original_call = ::get(); + let some_calculation = original_call + some_val; + // this next storage call is unnecessary and is wasteful + let unnecessary_call = ::get(); + // should've just used first_call here because u32 is copy + let another_calculation = some_calculation + unnecessary_call; + ::put(another_calculation); + let now = >::block_number(); + Self::deposit_event(RawEvent::InefficientValueChange(another_calculation, now)); + Ok(()) +} +``` + +Instead, the initial call value should be reused. In this example, the `SomeCopyValue` value is [`Copy`](https://doc.rust-lang.org/std/marker/trait.Copy.html) so we should prefer the following code without the unnecessary second call to storage: + +```rust +fn swap_value_w_copy(origin, some_val: u32) -> Result { + let _ = ensure_signed(origin)?; + let original_call = ::get(); + let some_calculation = original_call + some_val; + // uses the original_call because u32 is copy + let another_calculation = some_calculation + original_call; + ::put(another_calculation); + let now = >::block_number(); + Self::deposit_event(RawEvent::InefficientValueChange(another_calculation, now)); + Ok(()) +} +``` + +If the type was not `Copy`, but was [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html), then it is still preferred to clone the value in the method than to make another call to runtime storage. + +```rust +decl_storage! { + trait Store for Module as StorageCache { + // ...... + // clone type + KingMember get(king_member): T::AccountId; + GroupMembers get(group_members): Vec; + } +} +``` + +The runtime methods enable the calling account to swap the `T::AccountId` value in storage if +1. the existing storage value is not in `GroupMembers` AND +2. the calling account is in `Group Members + +The first implementation makes a second unnecessary call to runtime storage instead of cloning the call for `existing_key`: +```rust +fn swap_king_no_cache(origin) -> Result { + let new_king = ensure_signed(origin)?; + let existing_king = >::get(); + + // only places a new account if + // (1) the existing account is not a member && + // (2) the new account is a member + ensure!(!Self::is_member(existing_king), "is a member so maintains priority"); + ensure!(Self::is_member(new_king.clone()), "not a member so doesn't get priority"); + + // BAD (unnecessary) storage call + let old_king = >::get(); + // place new king + >::put(new_king.clone()); + + Self::deposit_event(RawEvent::InefficientKingSwap(old_king, new_king)); + Ok(()) +} +``` + +If the `existing_key` is used without a `clone` in the event emission instead of `old_king`, then the compiler returns the following error + +```bash +error[E0382]: use of moved value: `existing_king` + --> src/lib.rs:93:63 + | +80 | let existing_king = >::get(); + | ------------- move occurs because `existing_king` has type `::AccountId`, which does not implement the `Copy` trait +... +85 | ensure!(!Self::is_member(existing_king), "is a member so maintains priority"); + | ------------- value moved here +... +93 | Self::deposit_event(RawEvent::InefficientKingSwap(existing_king, new_king)); + | ^^^^^^^^^^^^^ value used here after move + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. +error: Could not compile `storage-cache`. + +To learn more, run the command again with --verbose. +``` + +Fixing this only requires cloning the original call to storage before it is moved: + +```rust +fn swap_king_with_cache(origin) -> Result { + let new_king = ensure_signed(origin)?; + let existing_king = >::get(); + // clone before existing_king is moved + let old_king = existing_king.clone(); + + // existing king is moved next + ensure!(!Self::is_member(existing_king), "is a member so maintains priority"); + ensure!(Self::is_member(new_king.clone()), "not a member so doesn't get priority"); + + // + // place new king + >::put(new_king.clone()); + + // use cached old_king value here + Self::deposit_event(RawEvent::BetterKingSwap(old_king, new_king)); + Ok(()) +} +``` + +Not all types implement [`Copy`](https://doc.rust-lang.org/std/marker/trait.Copy.html) or [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html), so it is important to discern other patterns that minimize and alleviate the cost of calls to storage. diff --git a/src/testing/README.md b/src/testing/README.md index 7dac8f22c..80b5c2f8d 100644 --- a/src/testing/README.md +++ b/src/testing/README.md @@ -1,7 +1,6 @@ # Module Testing * this section will be used to hold the information from `best-practices/errtesting` when that section inevitably outgrows a single recipe format (which is a priority) - * [Crypto Collectables Testing](https://www.shawntabrizi.com/substrate-collectables-workshop/#/5/introduction). Things to cover not covered in that example include From df8f8a5b2425055f44ca85b14128fe766c663f3e Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 30 Sep 2019 01:20:06 +0200 Subject: [PATCH 19/23] set storage and iteration --- .../{append-to-vec => vec-set}/Cargo.toml | 4 +- .../{append-to-vec => vec-set}/src/lib.rs | 32 ++++++- src/base/rust.md | 2 +- src/storage/iterate.md | 93 +++++++++++++++++++ 4 files changed, 125 insertions(+), 6 deletions(-) rename kitchen/modules/{append-to-vec => vec-set}/Cargo.toml (96%) rename kitchen/modules/{append-to-vec => vec-set}/src/lib.rs (58%) diff --git a/kitchen/modules/append-to-vec/Cargo.toml b/kitchen/modules/vec-set/Cargo.toml similarity index 96% rename from kitchen/modules/append-to-vec/Cargo.toml rename to kitchen/modules/vec-set/Cargo.toml index a506f6979..c58f7ee2a 100644 --- a/kitchen/modules/append-to-vec/Cargo.toml +++ b/kitchen/modules/vec-set/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "append-to-vec" +name = "vec-set" version = "0.1.0" authors = ["4meta5"] edition = "2018" @@ -17,7 +17,7 @@ std = [ [dependencies.parity-scale-codec] default-features = false features = ['derive'] -version = '1.0.5' +version = '1.0.6' [dependencies.support] default_features = false diff --git a/kitchen/modules/append-to-vec/src/lib.rs b/kitchen/modules/vec-set/src/lib.rs similarity index 58% rename from kitchen/modules/append-to-vec/src/lib.rs rename to kitchen/modules/vec-set/src/lib.rs index 713b0696e..029b9df02 100644 --- a/kitchen/modules/append-to-vec/src/lib.rs +++ b/kitchen/modules/vec-set/src/lib.rs @@ -2,7 +2,7 @@ // demonstrates how to use append instead of mutate // https://crates.parity.io/srml_support/storage/trait.StorageValue.html#tymethod.append -use support::{decl_module, decl_storage, decl_event, StorageValue, dispatch::Result}; +use support::{ensure, decl_module, decl_storage, decl_event, StorageValue, dispatch::Result}; use system::ensure_signed; pub trait Trait: system::Trait { @@ -11,6 +11,7 @@ pub trait Trait: system::Trait { decl_storage! { trait Store for Module as VecMap { + Members get(members): Vec; CurrentValues get(current_values): Vec; NewValues get(new_values): Vec; } @@ -18,6 +19,8 @@ decl_storage! { decl_event!( pub enum Event where AccountId = ::AccountId { + // added member + MemberAdded(AccountId), // mutate to append MutateToAppend(AccountId), // append @@ -49,6 +52,29 @@ decl_module! { current_values.append(&mut Self::new_values()); Self::deposit_event(RawEvent::AppendVec(user)); Ok(()) - } // more examples in srml/elections-phragmen + } // more examples in srml/elections-phragmen + + fn add_member(origin) -> Result { + let new_member = ensure_signed(origin)?; + ensure!(!Self::is_member(&new_member), "must not be a member to be added"); + let mut members = >::get(); + members.append(&mut vec![new_member.clone()]); + Self::deposit_event(RawEvent::MemberAdded(new_member)); + Ok(()) + } + + fn member_removed(origin) -> Result { + let old_member = ensure_signed(origin)?; + ensure!(Self::is_member(&old_member), "must be a member in order to exit"); + >::mutate(|v| v.retain(|i| i != &old_member)); + Ok(()) + } + // also see `append_or_insert`, `append_or_put` in srml-elections/phragmen, democracy } -}// todo: append_or_insert, append_or_put \ No newline at end of file +} + +impl Module { + pub fn is_member(who: &T::AccountId) -> bool { + >::get().contains(who) + } // TODO: child trie for more efficient membership storage structure +} \ No newline at end of file diff --git a/src/base/rust.md b/src/base/rust.md index c69e733a7..5fdbf813f 100644 --- a/src/base/rust.md +++ b/src/base/rust.md @@ -13,7 +13,7 @@ To become more familiar with commmon design patterns in Rust, the following link To optimize runtime performance, Substrate developers should make use of iterators, traits, and Rust's other "*zero cost* abstractions": * [Abstraction without overhead: traits in Rust](https://blog.rust-lang.org/2015/05/11/traits.html), [related conference talk](https://www.youtube.com/watch?v=Sn3JklPAVLk) -* [Effectively Using Iterators in Rust](https://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html), *[iterate over a slice rather than a `vec!`](https://twitter.com/heinz_gies/status/1121490424739303425)* +* [Effectively Using Iterators in Rust](https://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html) * [Achieving Warp Speed with Rust](http://troubles.md/posts/rust-optimization/) It is not (immediately) necessary to become familiar with multithreading because the runtime operates in a [single-threaded context](https://www.tutorialspoint.com/single-threaded-and-multi-threaded-processes). Even so, an optimized [substrate node](https://github.com/paritytech/substrate/tree/master/node) architecture will use a custom RPC interface. Moreover, the runtime might take advantage of the [offchain workers API](https://substrate.dev/docs/en/next/overview/off-chain-workers) to minimize the computation executed on-chain. Effectively using these features requires increased familiarity with advanced Rust. diff --git a/src/storage/iterate.md b/src/storage/iterate.md index e69de29bb..269f152ca 100644 --- a/src/storage/iterate.md +++ b/src/storage/iterate.md @@ -0,0 +1,93 @@ +# Set Storage and Iteration +*[`kitchen/modules/vec-set`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/vec-set)* + +Storing a vector in the runtime can often be useful for managing groups and verifying membership. This recipe discusses common patterns encounted when storing vectors in runtime storage. + +* [verifying group membership](#group) +* [Append vs Mutate](#append) +* [Iteration in the Runtime](#iterate) + +## Verifying Group Membership + +To maintain a set of `AccountId` to establish group ownership of decisions, it is straightforward to store a vector in the runtime of `AccountId`. + +```rust +decl_storage! { + trait Store for Module as VecMap { + Members get(members): Vec; + } +} +``` + +It is easy to add the following helper method to verify membership elsewhere in the runtime. + +```rust +impl Module { + fn is_member(who: &T::AccountId) -> bool { + >::get().contains(who) + } +} +``` + +This helper method can be placed in other runtime methods to restrict certain changes to runtime storage to privileged groups. Depending on the incentive structure of the network/chain, the members in these groups may have earned membership and the subsequent access rights through loyal contributions to the system. + +```rust +// use support::ensure +fn member_action(origin) -> Result { + let member = ensure_signed(origin)?; + ensure!(Self::is_member(&member), "not a member => cannot do action"); + // + Ok(()) +} +``` + +In this example, the helper method facilitates isolation of runtime storage access rights according to membership. In general, **place [`ensure!`](https://crates.parity.io/srml_support/macro.ensure.html) checks at the top of each runtime function's logic to verify that all of the requisite checks pass before performing any storage changes.** *Note that this is similar to [`require()`](https://ethereum.stackexchange.com/questions/15166/difference-between-require-and-assert-and-the-difference-between-revert-and-thro) checks at the top of function bodies in Solidity contracts.* + +> NOTE: *[child trie](https://github.com/substrate-developer-hub/recipes/issues/35) storage provides a more efficient data structure for tracking group membership* + +## Append vs. Mutate + +```rust +decl_storage! { + trait Store for Module as VecMap { + CurrentValues get(current_values): Vec; + NewValues get(new_values): Vec; + } +} +``` + +Before [3071](https://github.com/paritytech/substrate/pull/3071) was merged, it was necessary to call [`mutate`](https://crates.parity.io/srml_support/storage/trait.StorageValue.html#tymethod.mutate) to push new values to a vector stored in runtime storage. + +```rust +fn mutate_to_append(origin) -> Result { + let user = ensure_signed(origin)?; + + // this decodes the existing vec, appends the new values, and re-encodes the whole thing + ::mutate(|v| v.extend_from_slice(&Self::new_values())); + Self::deposit_event(RawEvent::MutateToAppend(user)); + Ok(()) +} +``` + +For vectors stored in the runtime, mutation can be relatively expensive. This follows from the fact that [`mutate`](https://crates.parity.io/srml_support/storage/trait.StorageValue.html#tymethod.mutate) entails decoding the vector, making changes, and re-encoding the whole vector. It seems wasteful to decode the entire vector, push a new item, and then re-encode the whole thing. This provides sufficient motivation for [`append`](https://crates.parity.io/srml_support/storage/trait.StorageValue.html#tymethod.append): + + +```rust +fn append_new_entries(origin) -> Result { + let user = ensure_signed(origin)?; + + // this encodes the new values and appends them to the already encoded existing evc + let mut current_values = Self::current_values(); + current_values.append(&mut Self::new_values()); + Self::deposit_event(RawEvent::AppendVec(user)); + Ok(()) +} +``` + +[`append`](https://crates.parity.io/srml_support/storage/trait.StorageValue.html#tymethod.append) encodes the new values, and pushes them to the already encoded vector without decoding the existing entries. This method removes the unnecessary steps for decoding and re-encoding the unchanged elements. + +## Iteration in the Runtime + +In general, iteration in the runtime should be avoided. *In the future*, [offchain-workers](https://github.com/substrate-developer-hub/recipes/issues/45) may provide a less expensive way to iterate over runtime storage items. Moreover, *[child tries](https://github.com/substrate-developer-hub/recipes/issues/35)* enable cheap inclusion proofs without the same lookup costs associated with vectors. + +Even so, there are a few tricks to alleviate the costs of iterating over runtime storage items like vectors. For example, it is [cheaper to iterate over a slice](https://twitter.com/heinz_gies/status/1121490424739303425) than a vector. With this in mind, store items in the runtime as vectors and transform them into slices after making storage calls. [3041](https://github.com/paritytech/substrate/pull/3041) introduced `insert_ref` and `put_ref` in order to allow equivalent reference-style types to be placed without copy (e.g. a storage item of `Vec` can now be written from a `&[AccountId]`). This enables greater flexibility when working with slices that are associated with vectors stored in the runtime. \ No newline at end of file From feb62e6ce00b4310c9c88b9d515b7eae953b1129 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 30 Sep 2019 02:27:37 +0200 Subject: [PATCH 20/23] writing for double map recipe --- kitchen/modules/child-trie/src/lib.rs | 5 +- kitchen/modules/linked-map/Cargo.toml | 2 +- kitchen/modules/linked-map/src/lib.rs | 19 ++- .../modules/module-constant-config/Cargo.toml | 2 +- src/storage/README.md | 6 +- src/storage/childtries.md | 4 + src/storage/constants.md | 1 + src/storage/double.md | 39 ++++++ src/storage/enumerated.md | 121 ++++-------------- 9 files changed, 86 insertions(+), 113 deletions(-) diff --git a/kitchen/modules/child-trie/src/lib.rs b/kitchen/modules/child-trie/src/lib.rs index 0a3b629d3..a973cd632 100644 --- a/kitchen/modules/child-trie/src/lib.rs +++ b/kitchen/modules/child-trie/src/lib.rs @@ -1,4 +1 @@ -// FROM ROB: What we're doing there is storing the root of another trie under a key in the -// main trie. Since keys are typically hashed in the main trie, it's a fairly -// convenient (although not the only) way to make sure that inserting many elements -// under a single mapping does not make your typical lookup path for unrelated elements longer \ No newline at end of file +// child trie storage example \ No newline at end of file diff --git a/kitchen/modules/linked-map/Cargo.toml b/kitchen/modules/linked-map/Cargo.toml index 47f08f767..edf19829e 100644 --- a/kitchen/modules/linked-map/Cargo.toml +++ b/kitchen/modules/linked-map/Cargo.toml @@ -17,7 +17,7 @@ std = [ [dependencies.parity-scale-codec] default-features = false features = ['derive'] -version = '1.0.5' +version = '1.0.6' [dependencies.support] default_features = false diff --git a/kitchen/modules/linked-map/src/lib.rs b/kitchen/modules/linked-map/src/lib.rs index e5a623341..913ace79c 100644 --- a/kitchen/modules/linked-map/src/lib.rs +++ b/kitchen/modules/linked-map/src/lib.rs @@ -42,19 +42,17 @@ decl_module! { let who = ensure_signed(origin)?; // increment the counter - ::mutate(|count| *count + 1); + let new_count = ::get() + 1; // add member at the largest_index - let new_largest_index = ::get() + 1; - >::insert(new_largest_index, who.clone()); + >::insert(new_count, who.clone()); // incremement counter - ::put(new_largest_index); + ::put(new_count); - // (same for linked counter) - let new_linked_largest_index = ::get() + 1; - >::insert(new_linked_largest_index, who.clone()); + // (keep linked list synced) + >::insert(new_count, who.clone()); // increment the counter - ::put(new_linked_largest_index); + ::put(new_count); Self::deposit_event(RawEvent::MemberAdded(who)); @@ -68,6 +66,7 @@ decl_module! { // verify existence ensure!(>::exists(index), "an element doesn't exist at this index"); + // for event emission (could be removed to minimize calls) let removed_member = >::get(index); >::remove(index); // assumes that we do not need to adjust the list because every add just increments counter @@ -96,7 +95,7 @@ decl_module! { } // pop >::remove(largest_index); - ::mutate(|count| *count - 1); + ::put(largest_index - 1); Self::deposit_event(RawEvent::MemberRemoved(member_to_remove.clone())); @@ -113,7 +112,7 @@ decl_module! { let head_index = >::head().unwrap(); let member_to_remove = >::take(index); - let head_member = >::get(head_index); + let head_member = >::take(head_index); >::insert(index, head_member); >::insert(head_index, member_to_remove); >::remove(head_index); diff --git a/kitchen/modules/module-constant-config/Cargo.toml b/kitchen/modules/module-constant-config/Cargo.toml index c7b3ad76c..e7839b23d 100644 --- a/kitchen/modules/module-constant-config/Cargo.toml +++ b/kitchen/modules/module-constant-config/Cargo.toml @@ -17,7 +17,7 @@ std = [ [dependencies.parity-scale-codec] default-features = false features = ['derive'] -version = '1.0.5' +version = '1.0.6' [dependencies.support] default_features = false diff --git a/src/storage/README.md b/src/storage/README.md index 1b17dfa6f..65684111f 100644 --- a/src/storage/README.md +++ b/src/storage/README.md @@ -8,12 +8,12 @@ More generally, blockchains provide an interface to store and interact with data Arbitrary data may be stored, as long as its data type is serializable in Substrate i.e. implements [`Encode`](https://docs.rs/parity-scale-codec/1.0.6/parity_scale_codec/#encode) and [`Decode`](https://docs.rs/parity-scale-codec/1.0.6/parity_scale_codec/#decode) traits. -The previous *[single-value storage](../basics/value.md)* showed how a single value can be stored in runtime storage. In this section, we cover +The previous *[single-value storage recipe](../basics/value.md)* showed how a single value can be stored in runtime storage. In this section, we cover - [caching values rather than calling to storage multiple times](./cache.md) - [storing sets, checking membership, and iteration](./iterate.md) - [ordered lists with basic maps and linked maps](./enumerated.md) -- [efficient subgroup removal by key prefix with double maps](./storage/double.md) +- [efficient subgroup removal by key prefix with double maps](./double.md) - [configurable module constants](./constants.md) *in-progress* -* - [cheap inclusion proofs with child tries](./childtries.md) \ No newline at end of file +- [cheap inclusion proofs with child tries](./childtries.md) \ No newline at end of file diff --git a/src/storage/childtries.md b/src/storage/childtries.md index e69de29bb..730e1a159 100644 --- a/src/storage/childtries.md +++ b/src/storage/childtries.md @@ -0,0 +1,4 @@ +# Cheap Inclusion Proofs: Child Tries +*[`kitchen/modules/child-trie`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/child-trie)* + +TODO: *[relevant issue (priority)](https://github.com/substrate-developer-hub/recipes/issues/35)* \ No newline at end of file diff --git a/src/storage/constants.md b/src/storage/constants.md index f56e682de..acf4afaf6 100644 --- a/src/storage/constants.md +++ b/src/storage/constants.md @@ -1,4 +1,5 @@ # Configurable Module Constants +*[`kitchen/modules/module-constant-config`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/module-constant-config)* To declare constant values within a runtime, it is necessary to import the [`Get`](https://crates.parity.io/srml_support/traits/trait.Get.html) trait from the `support` module diff --git a/src/storage/double.md b/src/storage/double.md index e69de29bb..02558be6c 100644 --- a/src/storage/double.md +++ b/src/storage/double.md @@ -0,0 +1,39 @@ +# Efficent Subgroup Removal by Subkey: Double Maps +*[`kitchen/modules/double-map`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/double-map)* + +For some runtimes, it may be necessary to remove a subset of values in a key-value mapping. If the subset maintain an associated identifier type, this can be done in a clean way with the [`double_map`](https://crates.parity.io/srml_support/storage/trait.StorageDoubleMap.html) via the [`remove_prefix`](https://crates.parity.io/srml_support/storage/trait.StorageDoubleMap.html#tymethod.remove_prefix) api. + +```rust +pub type GroupIndex = u32; // this is Encode (which is necessary for double_map) + +decl_storage! { + trait Store for Module as Dmap { + // member score (double map) + MemberScore: double_map GroupIndex, twox_128(T::AccountId) => u32; + // get group ID for member + GroupMembership get(group_membership): map T::AccountId => GroupIndex; + // for fast membership checks, see check-membership recipe for more details + AllMembers get(all_members): Vec; + } +} +``` + +For the purposes of this example, store the scores of each members in a map that associates this `u32` value with two keys: (1) the hash of the member's `AccountId` and (2) a `GroupIndex` identifier. This allows for efficient removal of all values associated with a specific `GroupIndex` identifier. + +```rust +fn remove_group_score(origin, group: GroupIndex) -> Result { + let member = ensure_signed(origin)?; + + let group_id = >::get(member); + // check that the member is in the group (could be improved by requiring n-of-m member support) + ensure!(group_id == group, "member isn't in the group, can't remove it"); + + // allows us to remove all group members from MemberScore at once + >::remove_prefix(&group_id); + + Self::deposit_event(RawEvent::RemoveGroup(group_id)); + Ok(()) +} +``` + +**Note**: It is necessary for one of the two keys to be hashed; *[TODO](https://github.com/substrate-developer-hub/recipes/issues/46)* \ No newline at end of file diff --git a/src/storage/enumerated.md b/src/storage/enumerated.md index ff5a7b9f4..e077e5bf4 100644 --- a/src/storage/enumerated.md +++ b/src/storage/enumerated.md @@ -1,68 +1,5 @@ -# Maps - -To use maps in the runtime storage, first import `StorageMap` from [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/) - -```rust -use support::{StorageMap}; -``` - -With this type, a key-value mapping (between `u32` types) can be stored in runtime storage using the following syntax - -```rust -decl_storage! { - trait Store for Module as Example { - MyMap: map u32 => u32; - } -} -``` - -Functions used to access a `StorageValue` are defined in [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/storage/generator.rs): - -```rust -/// Get the prefix key in storage. -fn prefix() -> &'static [u8]; - -/// Get the storage key used to fetch a value corresponding to a specific key. -fn key_for(x: &K) -> Vec; - -/// true if the value is defined in storage. -fn exists(key: &K, storage: &S) -> bool { - storage.exists(&Self::key_for(key)[..]) -} - -/// Load the value associated with the given key from the map. -fn get(key: &K, storage: &S) -> Self::Query; - -/// Take the value under a key. -fn take(key: &K, storage: &S) -> Self::Query; - -/// Store a value to be associated with the given key from the map. -fn insert(key: &K, val: &V, storage: &S) { - storage.put(&Self::key_for(key)[..], val); -} - -/// Remove the value under a key. -fn remove(key: &K, storage: &S) { - storage.kill(&Self::key_for(key)[..]); -} - -/// Mutate the value under a key. -fn mutate R, S: Storage>(key: &K, f: F, storage: &S) -> R; -``` - -To insert a `(key, value)` pair into a `StorageMap` named `MyMap`: - -```rust ->::insert(key, value); -``` - -To query `MyMap` for the `value` corresponding to a `key`: - -```rust -let value = >::get(key); -``` - -## Implementing Lists with Maps +# Lists: Maps vs Linked Maps +*[`kitchen/modules/linked-map`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/linked-map)* Substrate does not natively support a list type since it may encourage dangerous habits. Unless explicitly guarded against, a list will add unbounded `O(n)` complexity to an operation that will only charge `O(1)` fees ([Big O notation refresher](https://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/)). This opens an economic attack vector on your chain. @@ -87,27 +24,23 @@ This code allows us to store a list of participants in the runtime represented b This recipe answers those questions with snippets from relevant code samples: * [Adding/Removing Elements in an Unordered List](#unbounded) * [Swap and Pop for Ordered Lists](#swappop) -* [Linked Map for Simplified Runtime Logic](#linkedmap) +* [Linked Map for Simplified Enumeration](#linkedmap) - +**Note**: it is important to properly handle [overflow/underflow](../declarative/overunder.md) and verify [other relevant conditions](../declarative/README.md) for safety ## Adding/Removing Elements in an Unbounded List -If the size of the list is not relevant, the implementation is straightforward. *Note how it is still necessary to verify the existence of elements in the map before attempting access.* - -To add an `AccountId`, increment the `the_count` and insert an `AccountId` at that index: +If the size of the list is not relevant, the implementation is straightforward. To add an `AccountId`, increment the `the_counter` and insert an `AccountId` at that index: ```rust -// decl_module block fn add_member(origin) -> Result { let who = ensure_signed(origin)?; - // increment the counter - >::mutate(|count| *count + 1); - - // add member at the largest_index - let largest_index = >::get(); - >::insert(largest_index, who.clone()); + let new_count = >::get() + 1; + // insert new member at next highest index + >::insert(new_count, who.clone()); + // increment counter + >::put(new_count); Self::deposit_event(RawEvent::MemberAdded(who)); @@ -118,13 +51,14 @@ fn add_member(origin) -> Result { To remove an `AccountId`, call the `remove` method for the `StorageMap` type at the relevant index. In this case, it isn't necessary to update the indices of other `proposal`s; order is not relevant. ```rust -// decl_module block fn remove_member_unbounded(origin, index: u32) -> Result { let who = ensure_signed(origin)?; // verify existence ensure!(>::exists(index), "an element doesn't exist at this index"); + // for event emission let removed_member = >::get(index); + // remove member at provided index >::remove(index); Self::deposit_event(RawEvent::MemberRemoved(removed_member)); @@ -145,23 +79,22 @@ To preserve storage so that the list doesn't continue growing even after removin Use the *swap and pop* algorithm to remove elements from the list. ```rust -// decl_module block -fn remove_member_ordered(origin, index: u32) -> Result { - let who = ensure_signed(origin)?; +fn remove_member_bounded(origin, index: u32) -> Result { + let _ = ensure_signed(origin)?; ensure!(>::exists(index), "an element doesn't exist at this index"); - let largest_index = >::get(); + let largest_index = ::get(); let member_to_remove = >::take(index); // swap if index != largest_index { - let temp = >::take(largest_index); - >::insert(index, temp); - >::insert(largest_index, member_to_remove.clone()); + let temp = >::take(largest_index); + >::insert(index, temp); + >::insert(largest_index, member_to_remove.clone()); } // pop >::remove(largest_index); - >::mutate(|count| *count - 1); + ::mutate(|count| *count - 1); Self::deposit_event(RawEvent::MemberRemoved(member_to_remove.clone())); @@ -169,11 +102,9 @@ fn remove_member_ordered(origin, index: u32) -> Result { } ``` -*Keep the same logic for inserting proposals (increment `TheCount` and insert the entry at the head of the list)* - ### Linked Map -To trade performance for *relatively* simple code, utilize the `linked_map` data structure. By implementing [`EnumarableStorageMap`](https://crates.parity.io/srml_support/storage/trait.EnumerableStorageMap.html) in addition to [`StorageMap`](https://crates.parity.io/srml_support/storage/trait.StorageMap.html), `linked_map` provides a method `head` which yields the head of the *list*, thereby making it unnecessary to also store the `LargestIndex`. The `enumerate` method also returns an `Iterator` ordered according to when `(key, value)` pairs were inserted into the map. +To trade performance for *relatively* simple code, use the `linked_map` data structure. By implementing [`EnumarableStorageMap`](https://crates.parity.io/srml_support/storage/trait.EnumerableStorageMap.html) in addition to [`StorageMap`](https://crates.parity.io/srml_support/storage/trait.StorageMap.html), `linked_map` provides a method `head` which yields the head of the *list*, thereby making it unnecessary to also store the `LargestIndex` (the *counters*). The `enumerate` method also returns an `Iterator` ordered according to when `(key, value)` pairs were inserted into the map. To use `linked_map`, import `EnumerableStorageMap`. Here is the new declaration in the `decl_storage` block: @@ -188,23 +119,25 @@ decl_storage! { } ``` -The `add_member_linked` method is logically equivalent to the previous `add` method. Here is the new `remove_member_linked` method: +The method adding members is no different than the previously covered method, but the `remove_member_linked` method expresses swap and pop in a different way ```rust -// decl_module block fn remove_member_linked(origin, index: u32) -> Result { - let who = ensure_signed(origin)?; + let _ = ensure_signed(origin)?; ensure!(>::exists(index), "A member does not exist at this index"); let head_index = >::head().unwrap(); + // swap let member_to_remove = >::take(index); - let head_member = >::get(head_index); + let head_member = >::take(head_index); >::insert(index, head_member); + >::insert(head_index, member_to_remove); + // pop >::remove(head_index); Ok(()) } ``` -The only caveat is that this implementation incurs some performance costs (vs solely using `StorageMap` and `StorageValue`) because `linked_map` heap allocates the entire map as an iterator in order to implement the [`enumerate` method](https://crates.parity.io/srml_support/storage/trait.EnumerableStorageMap.html#tymethod.enumerate). \ No newline at end of file +This implementation incurs some performance costs (vs solely using `StorageMap` and `StorageValue`) because `linked_map` heap allocates the entire map as an iterator in order to implement the [`enumerate` method](https://crates.parity.io/srml_support/storage/trait.EnumerableStorageMap.html#tymethod.enumerate). \ No newline at end of file From b1a7cb31eea72c66111be49f5e98daf7cd56c22e Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 30 Sep 2019 12:10:51 +0200 Subject: [PATCH 21/23] lots of changes, not ready to merge --- .../modules/currency-imbalances/Cargo.toml | 4 +- kitchen/modules/lockable-currency/Cargo.toml | 4 +- src/SUMMARY.md | 5 +- src/declarative/README.md | 142 ++---------------- src/declarative/collide.md | 19 +++ src/declarative/ensure.md | 42 ++++++ src/declarative/enums.md | 49 ++++++ src/declarative/overunder.md | 0 src/declarative/safety_readme.md | 23 --- src/declarative/verify.md | 25 +++ src/design/README.md | 8 +- src/design/econsecurity.md | 14 +- src/design/errtesting.md | 1 - src/design/ttraits.md | 26 ++-- 14 files changed, 170 insertions(+), 192 deletions(-) create mode 100644 src/declarative/ensure.md delete mode 100644 src/declarative/overunder.md delete mode 100644 src/declarative/safety_readme.md diff --git a/kitchen/modules/currency-imbalances/Cargo.toml b/kitchen/modules/currency-imbalances/Cargo.toml index b7bbb2b2b..6d07fe2bd 100644 --- a/kitchen/modules/currency-imbalances/Cargo.toml +++ b/kitchen/modules/currency-imbalances/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "imbalances-example" +name = "currency-imbalances" version = "0.1.0" authors = ["4meta5"] edition = "2018" @@ -17,7 +17,7 @@ std = [ [dependencies.parity-scale-codec] default-features = false features = ['derive'] -version = '1.0.5' +version = '1.0.6' [dependencies.support] default_features = false diff --git a/kitchen/modules/lockable-currency/Cargo.toml b/kitchen/modules/lockable-currency/Cargo.toml index afbc37800..040b1dfdf 100644 --- a/kitchen/modules/lockable-currency/Cargo.toml +++ b/kitchen/modules/lockable-currency/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lockable-example" +name = "lockable-currency" version = "0.1.0" authors = ["4meta5"] edition = "2018" @@ -17,7 +17,7 @@ std = [ [dependencies.parity-scale-codec] default-features = false features = ['derive'] -version = '1.0.5' +version = '1.0.6' [dependencies.support] default_features = false diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f16c3b39f..00aa718c5 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -19,11 +19,10 @@ - [Economic Security](./design/econ_cost.md) - [Runtime Errors and Testing](./design/errtesting.md) - [Condition Oriented Programming](./declarative/README.md) + - [Ensure Checks](./declarative/ensure.md) - [Fixed Point Arithmetic](./declarative/fpamath.md) - - [Verifying Signed Messages](./declarative/verify.md) + - [Verify Signed Messages](./declarative/verify.md) - [Check for Collisions](./declarative/collide.md) - - [Check for Overflow/Underflow](./declarative/overunder.md) - - [Manage State with Enums](./declarative/enums.md) - [Runtime Configuration](./base/runtime.md) - [Simple Treasury](./dao/README.md) - [Token Curated Registry](./token/README.md) diff --git a/src/declarative/README.md b/src/declarative/README.md index 1160b2bbb..f177953d4 100644 --- a/src/declarative/README.md +++ b/src/declarative/README.md @@ -1,137 +1,17 @@ -## Declarative Programming +# Safety First -Within each runtime module function, it is important to perform all checks prior to any storage changes. When coding on most smart contract platforms, the stakes are lower because panics on contract calls will revert any storage changes. Conversely, Substrate requires greater attention to detail because mid-function panics will persist any prior changes made to storage. +Unlike conventional software development kits that abstract away low-level decisions, Substrate grants developers fine-grain control over the underlying implementation. This approach fosters high-performance, modular applications. At the same time, it also demands increased attention from developers. **With great power comes great responsibility**. -* [Using the Ensure Macro](#ensure) +Indeed, Substrate developers have to exercise incredible caution. The bare-metal control that they maintain over the runtime logic introduces new attack vectors. In the context of blockchains, the cost of bugs scale with the amount of capital secured by the application. Likewise, developers should *generally* abide by a few [rules](#criteria) when building with Substrate. These rules may not hold in every situation; Substrate offers optimization in context. -## Using the Ensure Macro +* [some heuristics](#criteria) +* [ensure checks](./ensure.md) +* [fixed point arithmetic](./fpamath.md) +* [verify signed messages](./declarative/verify.md) +* [check for collisions](./declarative/collide.md) -**Substrate developers should use [`ensure!`](https://crates.parity.io/srml_support/macro.ensure.html) checks at the top of each runtime function's logic to verify that all of the requisite checks pass before performing any storage changes.** *Note that this is similar to [`require()`](https://ethereum.stackexchange.com/questions/15166/difference-between-require-and-assert-and-the-difference-between-revert-and-thro) checks at the top of function bodies in Solidity contracts.* +## Module Development Criteria -The [Social Network](../storage/social.md#naive) recipe demonstrated how we can create separate runtime methods to verify necessary conditions in the main methods. +1. Modules should be relatively independent pieces of code; if your module is tied to many other modules, it should be a smart contract. See the [substrate-contracts-workshop](https://github.com/shawntabrizi/substrate-contracts-workshop) for more details with respect to smart contract programming on Substrate. Also, *use traits for abstracting shared behavior*. -```rust -impl Module { - pub fn friend_exists(current: T::AccountId, friend: T::AccountId) -> bool { - // search for friend in AllFriends vector - >::get(current).iter() - .any(|&ref a| a == &friend) - } - - pub fn is_blocked(current: T::AccountId, other_user: T::AccountId) -> bool { - // search for friend in Blocked vector - >::get(current).iter() - .any(|&ref a| a == &other_user) - } -} -``` - -"*By returning `bool`, we can easily use these methods in `ensure!` statements to verify relevant state conditions before making requests in the main runtime methods.*" - -```rust -// in the remove_friend method -ensure!(Self::friend_exists(user.clone(), old_friend.clone()), "old friend is not a friend"); - -... -// in the block method -ensure!(!Self::is_blocked(user.clone(), blocked_user.clone()), "user is already blocked"); -``` - -Indeed, this pattern of extracting runtime checks into separate functions and invoking the `ensure` macro in their place is useful. It produces readable code and encourages targeted testing to more easily identify the source of logic errors. - -*For a deeper dive into the "Verify First, Write Last" pattern, see the relevant section in the [Substrate Collectables tutorial](https://github.com/shawntabrizi/substrate-collectables-workshop/blob/master/3/buying-a-kitty.md#remember-verify-first-write-last) as well as [Substrate Best Practices](https://docs.substrate.dev/docs/tcr-tutorial-best-practices). This [github comment](https://github.com/shawntabrizi/substrate-collectables-workshop/pull/55#discussion_r258147961) is also very useful for visualizing the declarative pattern in practice.* - -**Bonus Reading** -* [Design for Testability](https://blog.nelhage.com/2016/03/design-for-testability/) -* [Condition-Oriented Programming](https://www.parity.io/condition-oriented-programming/) -* [Declarative Smart Contracts](https://www.tokendaily.co/blog/declarative-smart-contracts) - -## Verifying Signed Messages - -It is often useful to designate some functions as permissioned and, therefore, accessible only to a defined group. In this case, we check that the transaction that invokes the runtime function is signed before verifying that the signature corresponds to a member of the permissioned set. - -```rust -let who = ensure_signed(origin)?; -ensure!(Self::is_member(&who), "user is not a member of the group"); -``` - -We can define `is_member` similar to the helper methods in the [Social Network](../storage/social.md#naive) recipe by defining a vector of `AccountId`s (`current_member`) that contains all members. We then search this vector for the `AccountId` in question within the body of the `is_member` method. - -```rust -impl Module { - pub fn is_member(who: &T::AccountId) -> bool { - Self::current_member().iter() - .any(|&ref a| a == who) - } -} -``` - -*To read more about checking for signed messages, see the relevant section in the [Substrate collectables tutorial](https://shawntabrizi.github.io/substrate-collectables-workshop/#/1/storing-a-value?id=checking-for-a-signed-message).* - -## Checking for Collisions - -Often times we may intend for keys to be unique identifiers that map to a specific storage item. In this case, it is necessary to check for collisions before adding new entries. - -For example, it is common to use the hash of an object as the unique identifier in a map defined in the `decl_storage` block. Before adding a new value to the map, check that the key (hash) doesn't already have an associated value in the map. If it does, it is necessary to decide between the new item and the existing item to prevent an inadvertent key collision. In most cases, the new value is rejected. - -```rust -fn insert_value(origin, hash: Hash, value: u32) { - // check that key doesn't have an associated value - ensure!( !(Self::map::exists(&hash)), "key already has an associated value" ); - - // add key-value pair - >::insert(hash, value); -} -``` - -*See how the [Substrate Collectables Tutorial](https://shawntabrizi.com/substrate-collectables-workshop/#/2/generating-random-data?id=checking-for-collision) covers this pattern.* - -## Ergonomic Enums - -In the [`utxo-workshop`](https://github.com/nczhu/utxo-workshop), the code utilizes an [enum](https://doc.rust-lang.org/rust-by-example/custom_types/enum.html) to manage a data race scenario in which a transaction could arrive before some transactions that it `require`s. - -*Alice sends Bob 100 units and Bob sends Eve 80 of those units. Let's assume that Bob's transaction is dependent upon Alice's transaction. If Alice's transaction takes a few more seconds to arrive, we do not want to throw out Bob's transaction. Instead of panicking, we should place Bob's transaction in a temporary queue and lock it for some defined time period.* - -To see this pattern in action, see the `check_transaction` runtime function: - -```rust -pub fn check_transaction(transaction: &Transaction) -> CheckResult<'_> -``` - -This function returns `CheckResult<'_>`. The type signature of `CheckResult`: - -```rust -pub type CheckResult<'a> = rstd::result::Result, &'static str>; -``` - -The type signature of `CheckInfo`: - -```rust -/// Information collected during transaction verification -pub enum CheckInfo<'a> { - /// Combined value of all inputs and outputs - Totals { input: Value, output: Value }, - - /// Some referred UTXOs were missing - MissingInputs(Vec<&'a H256>), -} -``` - -This reveals that in the event of a successful call, it returns either the `Total`s struct that can be easily decomposed to calculate leftover value and distribute it evenly among the authorities OR returns a wrapper around the missing UTXOs which were necessary for verification. Here's the code in `check_transaction` that expresses this logic: - -```rust -if missing_utxo.is_empty() { - ensure!( - total_input >= total_output, - "output value must not exceed input value" - ); - Ok(CheckInfo::Totals { - input: total_input, - output: total_input, - }) -} else { - Ok(CheckInfo::MissingInputs(missing_utxo)) -} -``` - -This pattern demonstrates one way to safely handle the common data race that occurs when a conditional transaction arrives in the transaction pool before the arrival of a transaction that it `require`s. *We can extract this pattern to safely handle conditional paths in our code for which panics are undesirable, but it is also preferrable to pause processing.* \ No newline at end of file +2. It should not be possible for your code to panic after storage changes. Poor error handling in Substrate can *brick* the blockchain, rendering it useless thereafter. With this in mind, it is very important to structure code according to declarative, condition-oriented design patterns. *See more in the [declarative programming](./cop.md) section.* \ No newline at end of file diff --git a/src/declarative/collide.md b/src/declarative/collide.md index e69de29bb..b225392b8 100644 --- a/src/declarative/collide.md +++ b/src/declarative/collide.md @@ -0,0 +1,19 @@ +# Checking for Collisions + +When using unique identifiers that depend on an absence of key collision, it is best to check for key collision before adding new entries. + +For example, assume an object's hash the unique identifier (key) in a map defined in the `decl_storage` block. Before adding a new `(key, value)` pair to the map, verify that the key (hash) does not already have an associated value in the map. + +```rust +fn insert_value(origin, hash: Hash, value: u32) { + // check that key doesn't have an associated value + ensure!( !(Self::map::exists(&hash)), "key already has an associated value" ); + + // add key-value pair + >::insert(hash, value); +} +``` + + If it does, it is necessary to decide between the new item and the existing item to prevent an inadvertent key collision. + +*See how the [Substrate Collectables Tutorial](https://shawntabrizi.com/substrate-collectables-workshop/#/2/generating-random-data?id=checking-for-collision) covers this pattern.* \ No newline at end of file diff --git a/src/declarative/ensure.md b/src/declarative/ensure.md new file mode 100644 index 000000000..331b59240 --- /dev/null +++ b/src/declarative/ensure.md @@ -0,0 +1,42 @@ +# Condition Oriented Programming + +Within each runtime module function, it is important to perform requisite checks prior to any storage changes. Unlike existing smart contract platforms, Substrate requires greater attention to detail because mid-function panics will persist any prior changes made to storage. + +**Place [`ensure!`](https://crates.parity.io/srml_support/macro.ensure.html) checks at the top of each runtime function's logic to verify that all requisite conditions are met before performing any storage changes.** *Note that this is similar to [`require()`](https://ethereum.stackexchange.com/questions/15166/difference-between-require-and-assert-and-the-difference-between-revert-and-thro) checks at the top of function bodies in Solidity contracts.* + +In the [set storage and iteration](../storage/iterate.md), a vector was stored in the runtime to allow for simple membership checks for methods only available to members. + +```rust +decl_storage! { + trait Store for Module as VecMap { + Members get(members): Vec; + } +} +... +impl Module { + fn is_member(who: &T::AccountId) -> bool { + >::get().contains(who) + } +} +``` + + +"*By returning `bool`, we can easily use these methods in `ensure!` statements to verify relevant state conditions before making requests in the main runtime methods.*" + +```rust +fn member_action(origin) -> Result { + let member = ensure_signed(origin)?; + ensure!(Self::is_member(&member), "not a member => cannot do action"); + // + Ok(()) +} +``` + +Indeed, this pattern of extracting runtime checks into separate functions and invoking the `ensure` macro in their place is useful. It produces readable code and encourages targeted testing to more easily identify the source of logic errors. + +*This [github comment](https://github.com/shawntabrizi/substrate-collectables-workshop/pull/55#discussion_r258147961) might help when visualizing declarative patterns in practice.* + +**Bonus Reading** +* [Design for Testability](https://blog.nelhage.com/2016/03/design-for-testability/) +* [Condition-Oriented Programming](https://www.parity.io/condition-oriented-programming/) +* [Declarative Smart Contracts](https://www.tokendaily.co/blog/declarative-smart-contracts) \ No newline at end of file diff --git a/src/declarative/enums.md b/src/declarative/enums.md index e69de29bb..cf9901939 100644 --- a/src/declarative/enums.md +++ b/src/declarative/enums.md @@ -0,0 +1,49 @@ +# Ergonomic Enums + +In the [`utxo-workshop`](https://github.com/nczhu/utxo-workshop), the code utilizes an [enum](https://doc.rust-lang.org/rust-by-example/custom_types/enum.html) to manage a data race scenario in which a transaction could arrive before some transactions that it `require`s. + +*Alice sends Bob 100 units and Bob sends Eve 80 of those units. Let's assume that Bob's transaction is dependent upon Alice's transaction. If Alice's transaction takes a few more seconds to arrive, we do not want to throw out Bob's transaction. Instead of panicking, we should place Bob's transaction in a temporary queue and lock it for some defined time period.* + +To see this pattern in action, see the `check_transaction` runtime function: + +```rust +pub fn check_transaction(transaction: &Transaction) -> CheckResult<'_> +``` + +This function returns `CheckResult<'_>`. The type signature of `CheckResult`: + +```rust +pub type CheckResult<'a> = rstd::result::Result, &'static str>; +``` + +The type signature of `CheckInfo`: + +```rust +/// Information collected during transaction verification +pub enum CheckInfo<'a> { + /// Combined value of all inputs and outputs + Totals { input: Value, output: Value }, + + /// Some referred UTXOs were missing + MissingInputs(Vec<&'a H256>), +} +``` + +This reveals that in the event of a successful call, it returns either the `Total`s struct that can be easily decomposed to calculate leftover value and distribute it evenly among the authorities OR returns a wrapper around the missing UTXOs which were necessary for verification. Here's the code in `check_transaction` that expresses this logic: + +```rust +if missing_utxo.is_empty() { + ensure!( + total_input >= total_output, + "output value must not exceed input value" + ); + Ok(CheckInfo::Totals { + input: total_input, + output: total_input, + }) +} else { + Ok(CheckInfo::MissingInputs(missing_utxo)) +} +``` + +This pattern demonstrates one way to safely handle the common data race that occurs when a conditional transaction arrives in the transaction pool before the arrival of a transaction that it `require`s. *We can extract this pattern to safely handle conditional paths in our code for which panics are undesirable, but it is also preferrable to pause processing.* \ No newline at end of file diff --git a/src/declarative/overunder.md b/src/declarative/overunder.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/declarative/safety_readme.md b/src/declarative/safety_readme.md deleted file mode 100644 index ea0b3ef78..000000000 --- a/src/declarative/safety_readme.md +++ /dev/null @@ -1,23 +0,0 @@ -# Safety First - -Unlike conventional software development kits that abstract away low-level decisions, Substrate grants developers fine-grain control over the underlying implementation. This approach fosters high-performance, modular applications. At the same time, it also demands increased attention from developers. To quote the [late Uncle Ben](https://knowyourmeme.com/memes/with-great-power-comes-great-responsibility), **with great power comes great responsibility**. - -Indeed, Substrate developers have to exercise incredible caution. The bare-metal control that they maintain over the runtime logic introduces new attack vectors. In the context of blockchains, the cost of bugs scale with the amount of capital secured by the application. Likewise, developers should *generally* abide by a few [rules](#criteria) when building with Substrate. These rules may not hold in every situation; Substrate offers optimization in context. - -* [Module Development Criteria](#criteria) -* [Declarative Programming](./cop.md) -* [Optimizations](./optimizations.md) - -## Testing - -*Testing is not (yet) covered in the Substrate Recipes, but there is a great introduction to testing in the context of Substrate in the [Crypto Collectables Tutorial](https://www.shawntabrizi.com/substrate-collectables-workshop/#/5/setting-up-tests).* I also have enjoyed the following articles/papers on testing that apply to code organization more generally: -* [Conditional Compilation and Rust Unit Testing](https://os.phil-opp.com/unit-testing/) -* [Design for Testability](https://blog.nelhage.com/2016/03/design-for-testability/) -* [How I Test](https://blog.nelhage.com/2016/12/how-i-test/) -* [Simple Testing Can Prevent Most Critical Failures](https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf) - -## Module Development Criteria - -1. Modules should be independent pieces of code; if your module is tied to many other modules, it should be a smart contract. See the [substrate-contracts-workshop](https://github.com/shawntabrizi/substrate-contracts-workshop) for more details with respect to smart contract programming on Substrate. - -2. It should not be possible for your code to panic after storage changes. Poor error handling in Substrate can *brick* the blockchain, rendering it useless thereafter. With this in mind, it is very important to structure code according to declarative, condition-oriented design patterns. *See more in the [declarative programming](./cop.md) section.* \ No newline at end of file diff --git a/src/declarative/verify.md b/src/declarative/verify.md index e69de29bb..52c051450 100644 --- a/src/declarative/verify.md +++ b/src/declarative/verify.md @@ -0,0 +1,25 @@ +## Verifying Signed Messages + +It is often useful to designate some functions as permissioned and, therefore, accessible only to a defined group. In this case, we check that the transaction that invokes the runtime function is signed before verifying that the signature corresponds to a member of the permissioned set. + +```rust +let who = ensure_signed(origin)?; +ensure!(Self::is_member(&who), "user is not a member of the group"); +``` + +We can define `is_member` similar to the helper methods in the [Social Network](../storage/social.md#naive) recipe by defining a vector of `AccountId`s (`current_member`) that contains all members. We then search this vector for the `AccountId` in question within the body of the `is_member` method. + +```rust +impl Module { + pub fn is_member(who: &T::AccountId) -> bool { + Self::current_member().iter() + .any(|&ref a| a == who) + } +} +``` + +*To read more about checking for signed messages, see the relevant section in the [Substrate collectables tutorial](https://shawntabrizi.github.io/substrate-collectables-workshop/#/1/storing-a-value?id=checking-for-a-signed-message).* + +### Custom Origin + +* todo \ No newline at end of file diff --git a/src/design/README.md b/src/design/README.md index d69434e11..56d05113f 100644 --- a/src/design/README.md +++ b/src/design/README.md @@ -2,12 +2,6 @@ * [Types and Traits](./ttraits.md) * [Economic Security](./econsecurity.md) -* [Condition-Oriented Programming](./cop) * [Runtime Errors and Testing](./errtesting.md) -*short patterns* -* [Verifying Signed Messages](#verify) -* [Checking for Collisions](#collide) -* [Checking for Overflow/Underflow](#underflow) -* [Enums for Managing State](#enums) -* [Fixed Point Arithmetic](./fpamath.md) \ No newline at end of file +Next Section: *[Condition-Oriented Programming](../declarative/README.md)* \ No newline at end of file diff --git a/src/design/econsecurity.md b/src/design/econsecurity.md index 5aa79739c..b34a82f5c 100644 --- a/src/design/econsecurity.md +++ b/src/design/econsecurity.md @@ -1,11 +1,13 @@ # Efficiency => Security in Substrate -> Basically I need to make the point somewhere that efficiency influences security +An algorithm is considered to be *efficient* if its running time is polynomial in the size of the input, and *highly efficient* if its running time is linear in the size of the input. **It is important for all on-chain algorithms to be highly efficient, because they must scale linearly as the size of the Polkadot network grows**. In contrast, off-chain algorithms are only required to be efficient. - [Web3 Research](http://research.web3.foundation/en/latest/polkadot/NPoS/1.intro/) -We call an algorithm *efficient* if its running time is polynomial in the size of the input, and *highly efficient* if its running time is linear in the size of the input. It is important for all on-chain algorithms to be highly efficient, because they must scale linearly as the size of the Polkadot network grows. In contrast, off-chain algorithms are only required to be efficient. - [Web3 Research](http://research.web3.foundation/en/latest/polkadot/NPoS/1.intro/) +Moreover, any resources used by a transaction must explicitly be paid for within the module. If the resources used might be dependent on transaction parameters or pre-existing chain state, the in-module fee structure must adapt accordingly. Specifically, measuring the balance between **resources used** and **price paid** is an important design activity for runtime security. -*See [Substrate Best Practices](https://substrate.dev/docs/en/tutorials/tcr/) for more details on how efficiency influences the runtime's economic security.* +*Indeed, mispriced EVM operations have shown how operations that underestimate cost can open economic DOS attack vectors: [Onwards; Underpriced EVM Operations](https://www.parity.io/onwards/), [Under-Priced DOS Attacks on Ethereum](https://www4.comp.polyu.edu.hk/~csxluo/DoSEVM.pdf)* -**Related Reading** -* [Onwards; Underpriced EVM Operations](https://www.parity.io/onwards/), September 2016 -* [Under-Priced DOS Attacks on Ethereum](https://www4.comp.polyu.edu.hk/~csxluo/DoSEVM.pdf) \ No newline at end of file + \ No newline at end of file diff --git a/src/design/errtesting.md b/src/design/errtesting.md index 542b9d5df..d106cde20 100644 --- a/src/design/errtesting.md +++ b/src/design/errtesting.md @@ -27,7 +27,6 @@ impl module::Trait for Runtime { Within the context of testing, there are a few ways of building a mock runtime that offer varying levels of customizations. The easiest way is to mock runtime storage with - ```rust ``` diff --git a/src/design/ttraits.md b/src/design/ttraits.md index e29832db1..b94231810 100644 --- a/src/design/ttraits.md +++ b/src/design/ttraits.md @@ -1,20 +1,19 @@ -# Substrate Types and Traits +# Substrate Types and Traits +*[`kitchen/modules/lockable-currency`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/lockable-currency), [`kitchen/modules/reservable-currency`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/reservable-currency), [`kitchen/modules/currency-imbalances`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/currency-imbalances)* -To access Substrate specific types, the module's `Trait` must inherit from the [SRML](https://github.com/paritytech/substrate/tree/master/srml). For example, to access the Substrate types `Hash`, `AccountId`, and `BlockNumber`, it is sufficient to inherit the [`system`](https://github.com/paritytech/substrate/tree/master/srml/system) module: +To access **substrate specific types**, the module's `Trait` may inherit from the [Substrate Runtime Module Library](https://github.com/paritytech/substrate/tree/master/srml). For example, to access the Substrate types `Hash`, `AccountId`, and `BlockNumber`, it is sufficient to inherit the [`system`](https://github.com/paritytech/substrate/tree/master/srml/system) module: ```rust pub trait Trait: system::Trait {} ``` -This provides access to the types `Hash`, `AccountId`, and `BlockNumber` anywhere that specifies the generic `` using `T::`. It also provides access to other useful types, declared in the `pub Trait {}` block in [`systems/src/lib.rs`](https://github.com/paritytech/substrate/blob/v1.0/srml/system/src/lib.rs). +This provides access to `Hash`, `AccountId`, and `BlockNumber` anywhere that specifies the generic `` using `T::`. It also provides access to other useful types, declared in the `pub Trait {}` block in [`systems/src/lib.rs`](https://github.com/paritytech/substrate/blob/v1.0/srml/system/src/lib.rs). ## support::traits -Unlike in smart contract development, the way to inherit shared behavior is not to directly import other modules. Instead, it is common to either implement the same logic in the new context or utilize a trait from [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/traits.rs) to guide the new implementation. By abstracting shared behavior from the runtime modules into [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/traits.rs), Substrate makes it easy to extract and enforce best practices in the runtime. You can find the trait documentation [here](https://crates.parity.io/srml_support/traits/index.html). *See [Using Balances](./currency.md) for an example of usage*. +Unlike in smart contract development, the way to inherit shared behavior is not to directly import other modules. Instead, it is common to either implement the same logic in the new context or utilize a trait from [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/traits.rs) to guide the new implementation. By abstracting shared behavior from the runtime modules into [`srml/support`](https://github.com/paritytech/substrate/blob/master/srml/support/src/traits.rs), Substrate makes it easy to extract and enforce best practices in the runtime. You can find the trait documentation [here](https://crates.parity.io/srml_support/traits/index.html). - - -# Currency Types and Locking Techniques +### currency types and collateral management patterns To use a balances type in the runtime, import the [`Currency`](https://crates.parity.io/srml_support/traits/trait.Currency.html) trait from `srml/support` @@ -25,7 +24,6 @@ use support::traits::Currency; The [`Currency`](https://crates.parity.io/srml_support/traits/trait.Currency.html) trait provides an abstraction over a fungible assets system. To use the behavior defined in [`Currency`](https://crates.parity.io/srml_support/traits/trait.Currency.html), include it in the trait bounds of a module type. ```rust -// included system::Trait inheritance because it's in my code pub trait Trait: system::Trait { type Currency: Currency; } @@ -85,8 +83,6 @@ pub fn unlock_funds(origin, amount: BalanceOf) -> Result { } ``` -*The full code can be found in [`collateral/reservable`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/reservable) in the [kitchen](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/README.md)* - ## Lockable Currency [`srml/staking`](https://github.com/paritytech/substrate/blob/master/srml/staking/src/lib.rs) similarly uses [`LockableCurrency`](https://crates.parity.io/srml_support/traits/trait.LockableCurrency.html) trait for more nuanced handling of capital locking based on time increments. This type can be very useful in the context of economic systems that enforce accountability by collateralizing fungible resources. Import this trait in the usual way @@ -129,8 +125,6 @@ fn lock_capital(origin, amount: BalanceOf) -> Result { } ``` -*The full code can be found in [`collateral/lockable`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/lockable) in the [kitchen](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/README.md).* - ## Imbalances Functions that alter balances return an object of the [`Imbalance`](https://crates.parity.io/srml_support/traits/trait.Imbalance.html) type to express how much account balances have been altered in aggregate. This is useful in the context of state transitions that adjust the total supply of the `Currency` type in question. @@ -153,10 +147,8 @@ pub fn reward_funds(origin, to_reward: T::AccountId, reward: BalanceOf) { } ``` -*The full code can be found in [`collateral/imbalances`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/imbalances) in the [kitchen](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/README.md).* - -## takeaways +## takeaway -The way by which we represent value in the runtime dictates both the security and flexibility of the underlying transactional system. Likewise, it is nice to be able to take advantage of Rust's [flexible trait system](https://blog.rust-lang.org/2015/05/11/traits.html) when building systems intended to rethink how we exchange information and value 🚀 +The way we represent value in the runtime dictates both the security and flexibility of the underlying transactional system. Likewise, it is convenient to be able to take advantage of Rust's [flexible trait system](https://blog.rust-lang.org/2015/05/11/traits.html) when building systems intended to rethink how we exchange information and value 🚀 -BONUS: *see [`OnDilution`](https://crates.parity.io/srml_support/traits/trait.OnDilution.html#tymethod.on_dilution)* \ No newline at end of file +BONUS: *see [`OnDilution`](https://crates.parity.io/srml_support/traits/trait.OnDilution.html#tymethod.on_dilution) runtime hook* \ No newline at end of file From 9b8f164c2b3d127611ecbb0af8304b02734e9d11 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 30 Sep 2019 15:02:24 +0200 Subject: [PATCH 22/23] prepare to merge, next stage build --- kitchen/modules/README.md | 50 ++++++++++++++++++++------------------ kitchen/runtimes/README.md | 11 ++++----- src/SUMMARY.md | 9 +++---- src/declarative/README.md | 10 ++++---- src/declarative/ensure.md | 2 +- src/declarative/fpamath.md | 11 +++------ src/declarative/verify.md | 2 +- src/identity/social.md | 6 +++-- src/nft/README.md | 2 +- src/proof/README.md | 2 +- 10 files changed, 52 insertions(+), 53 deletions(-) diff --git a/kitchen/modules/README.md b/kitchen/modules/README.md index 2a6eeb009..5cfd7d6fe 100644 --- a/kitchen/modules/README.md +++ b/kitchen/modules/README.md @@ -1,28 +1,30 @@ # Modules -- [ ] **UPDATE WITH DESCRIPTIONS AND BUILDS SHOWN** +procedural examples +* [Hello Substrate](./hello-substrate) +* [Adding Machine](./adding-machine) +* [Simple Event (not generic)](./simple-event) +* [Generic Event](./generic-event) +* [Single Value Storage](./single-value) +* [Simple Map](./simple-map) +* [Linked Map](./linked-map) +* [Double Map](./double-map) +* [Cache Multiple Storage Calls](./storage-cache) +* [Using Vectors for Managing Membership/Sets](./vec-set) +* [Storing Structs in the Runtime](./struct-storage) +* [Module Constant Configuration](./module-constant-config) -**Event**: effectively logging, scheduling, and reacting to events defined in `decl_event` -* [Adding Machine](./modules/adder/) -* [Simple Event (not generic)](./modules/simple-event) -* [Generic Event](./modules/generic-event) +examples with context +* [Basic Token](./token) +* [Check Membership](./check-membership) +* [Currency Imbalances Type](./currency-imbalances) +* [Lockable Currency](./lockable-currency) +* [Reservable Currency](./reservable-currency) +* [Simple Treasury](./treasury) +* [On Finalize](./schedule-on-finalize) -**Storage**: managing interactions with the on-chain storage via `decl_storage` -* [Single Value Storage](./modules/value) -* [Simple Map](./modules/simple-map) -* [List](./modules/list) -* [Double Map](./modules/double-map) -* [Child Trie](./modules/child-trie) -* [Offchain Workers](./modules/offchain-workers) - -**Traits and Types**: using substrate traits and types -* [Module Inheritance](./modules/inherit) -* [Configurable Module Constants](./modules/constants/) -- [Nested Structs](./nstructs) - -**Examples**: usage examples of the above patterns *with context* -- Currency Types and Locking Techniques::{[lockable](./lockable), [reservable](./reservable), [imbalances](./imbalances)} -- [Token Transfer](./token) -- [Permissioned Methods](./permissioned) -- [Blockchain Event Loop](./loop) -- [Social Network](./social) \ No newline at end of file +*wip* (see issues for more information) +* [custom origin](./custom-origin) +* [child-trie](./child-trie) +* [social network](./social-network) +* [srml-panic](./srml-panic) \ No newline at end of file diff --git a/kitchen/runtimes/README.md b/kitchen/runtimes/README.md index 086cb1a9f..313d9677c 100644 --- a/kitchen/runtimes/README.md +++ b/kitchen/runtimes/README.md @@ -1,11 +1,10 @@ -# Runtimes +# runtimes -* super-node -* loosely coupled modules -* larger examples of inter-module usage `=>` look at Basti's PR and push the limits of how many modules can be used in a runtime +- [ ] super-node with all module configurations +- [ ] loosely coupled modules +- [ ] larger examples of inter-module usage `=>` look at bkch's PR to know how many modules can be placed in the runtime -> watch Joshy work on loosely coupled modules +## references * https://github.com/JoshOrndorff/Marketplace - * https://github.com/JoshOrndorff/TicTacToe \ No newline at end of file diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 00aa718c5..6d7138f3a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -21,14 +21,13 @@ - [Condition Oriented Programming](./declarative/README.md) - [Ensure Checks](./declarative/ensure.md) - [Fixed Point Arithmetic](./declarative/fpamath.md) - - [Verify Signed Messages](./declarative/verify.md) - - [Check for Collisions](./declarative/collide.md) + - [Verify Signed Messages; Custom Origin](./declarative/verify.md) - [Runtime Configuration](./base/runtime.md) -- [Simple Treasury](./dao/README.md) -- [Token Curated Registry](./token/README.md) +- [Simple Treasury](./dao/treasury.md) +- [Token Curated Registry](./token/token.md) - [NFT Ticket Machine](./nft/README.md) - [Proof of Existence](./proof/README.md) -- [Social Network](./identity/README.md) +- [Social Network](./identity/social.md) - [Featured Tutorials](./base/dessert.md) ----------- diff --git a/src/declarative/README.md b/src/declarative/README.md index f177953d4..56311492a 100644 --- a/src/declarative/README.md +++ b/src/declarative/README.md @@ -1,14 +1,14 @@ -# Safety First +# Condition Oriented Programming Unlike conventional software development kits that abstract away low-level decisions, Substrate grants developers fine-grain control over the underlying implementation. This approach fosters high-performance, modular applications. At the same time, it also demands increased attention from developers. **With great power comes great responsibility**. -Indeed, Substrate developers have to exercise incredible caution. The bare-metal control that they maintain over the runtime logic introduces new attack vectors. In the context of blockchains, the cost of bugs scale with the amount of capital secured by the application. Likewise, developers should *generally* abide by a few [rules](#criteria) when building with Substrate. These rules may not hold in every situation; Substrate offers optimization in context. +Indeed, Substrate developers have to exercise incredible caution. The bare-metal control that they maintain over the runtime logic introduces new attack vectors. In the context of blockchains, the cost of bugs scale with the amount of capital secured by the application. Likewise, developers should generally abide by a few *[rules](#criteria)* when building with Substrate. These rules may not hold in every situation; Substrate offers optimization in context. -* [some heuristics](#criteria) +Each of the recipes in this section are oriented around increasing * [ensure checks](./ensure.md) * [fixed point arithmetic](./fpamath.md) -* [verify signed messages](./declarative/verify.md) -* [check for collisions](./declarative/collide.md) +* [verify signed messages](./verify.md) +* [checking for collisions](./collide.md) ## Module Development Criteria diff --git a/src/declarative/ensure.md b/src/declarative/ensure.md index 331b59240..0898989de 100644 --- a/src/declarative/ensure.md +++ b/src/declarative/ensure.md @@ -1,4 +1,4 @@ -# Condition Oriented Programming +# ensure! Within each runtime module function, it is important to perform requisite checks prior to any storage changes. Unlike existing smart contract platforms, Substrate requires greater attention to detail because mid-function panics will persist any prior changes made to storage. diff --git a/src/declarative/fpamath.md b/src/declarative/fpamath.md index df5246f06..7f7e0a0b5 100644 --- a/src/declarative/fpamath.md +++ b/src/declarative/fpamath.md @@ -1,17 +1,14 @@ # Fixed Point Arithmetic -*computers are notoriously bad with precision with small numbers* +> *[in progress](https://github.com/substrate-developer-hub/recipes/issues/12)* -* see https://github.com/substrate-developer-hub/recipes/issues/12 for links +Computers are imprecise with large numbers. Indeed, floating point arithmetic is not used in the runtime because it can introduce nondeterministic calculations. -## Perbill and Permill Usage - -## Saturating Operations +## Permill, Perbill, Fixed64... ## Checking for Overflows/Underflows -> **explain here how context matters for these checks -We can use the `checked` traits in [substrate-primitives](https://crates.parity.io/sr_primitives/traits/index.html) to protect against [overflow/underflow](https://medium.com/@taabishm2/integer-overflow-underflow-and-floating-point-imprecision-6ba869a99033) when incrementing/decrementing objects in our runtime. To follow the [Substrat collectable tutorial example](https://shawntabrizi.com/substrate-collectables-workshop/#/2/tracking-all-kitties?id=checking-for-overflowunderflow), we use [`checked_add()`](https://crates.parity.io/sr_primitives/traits/trait.CheckedAdd.html) to safely handle the possibility of overflow when incremementing a global counter. *Note that this check is similar to [`SafeMath`](https://ethereumdev.io/safemath-protect-overflows/) in Solidity*. +We can use the `checked` traits in [substrate-primitives](https://crates.parity.io/sr_primitives/traits/index.html) to protect against [overflow/underflow](https://medium.com/@taabishm2/integer-overflow-underflow-and-floating-point-imprecision-6ba869a99033) when incrementing/decrementing objects in our runtime. To follow the [Substrat collectable tutorial example](https://shawntabrizi.com/substrate-collectables-workshop/#/2/tracking-all-kitties?id=checking-for-overflowunderflow), use [`checked_add()`](https://crates.parity.io/sr_primitives/traits/trait.CheckedAdd.html) to safely handle the possibility of overflow when incremementing a global counter. *Note that this check is similar to [`SafeMath`](https://ethereumdev.io/safemath-protect-overflows/) in Solidity*. ```rust use runtime_primitives::traits::CheckedAdd; diff --git a/src/declarative/verify.md b/src/declarative/verify.md index 52c051450..525fa6575 100644 --- a/src/declarative/verify.md +++ b/src/declarative/verify.md @@ -1,4 +1,4 @@ -## Verifying Signed Messages +## Verifying Signed Messages It is often useful to designate some functions as permissioned and, therefore, accessible only to a defined group. In this case, we check that the transaction that invokes the runtime function is signed before verifying that the signature corresponds to a member of the permissioned set. diff --git a/src/identity/social.md b/src/identity/social.md index 10041ad9e..53ff1a23c 100644 --- a/src/identity/social.md +++ b/src/identity/social.md @@ -1,5 +1,7 @@ # Higher Order Arrays with Tuples and Maps +> *[related issue (wip)](https://github.com/substrate-developer-hub/recipes/issues/47)* + To represent ownership of multiple items across multiple users, tuples can be used alongside maps in order to emulate arrays. For example, consider a scenario in which persistent storage keeps track of a *social network graph* in which each user (represented by an `AccountId`) has a list of other friends. In this case, it would be convenient to use a 2 dimensional array like @@ -83,9 +85,9 @@ Similarly, when we block a user, we should check that the user isn't already blo ensure!(!Self::is_blocked(user.clone(), blocked_user.clone()), "user is already blocked"); ``` -The full logic for this sample can be found in the [kitchen](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen) in `storage/arrays`. +The full logic for this sample can be found in the [kitchen](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen) in `modules/social-network`. *To see another example of how to use tuples to emulate higher order arrays, see the [Substrate Collectables Tutorial](https://shawntabrizi.github.io/substrate-collectables-workshop/#/2/owning-multiple-kitties?id=using-tuples-to-emulate-higher-order-arrays).* -**NOTE**: [DoubleMap](https://crates.parity.io/srml_support/storage/trait.StorageDoubleMap.html) is a map with two keys; this storage item may also be useful for implementing higher order arrays \ No newline at end of file +**NOTE**: [DoubleMap](https://crates.parity.io/srml_support/storage/trait.StorageDoubleMap.html) is a map with two keys; this storage item may also be useful for implementing higher order arrays (see [the related recipe](../storage/double.md)) \ No newline at end of file diff --git a/src/nft/README.md b/src/nft/README.md index b68454848..601598adf 100644 --- a/src/nft/README.md +++ b/src/nft/README.md @@ -1,3 +1,3 @@ # NFT Ticket Machine -* kitties `=>` collectables `=>` ticketing \ No newline at end of file +*[related issue (wip)](https://github.com/substrate-developer-hub/recipes/issues/47)* \ No newline at end of file diff --git a/src/proof/README.md b/src/proof/README.md index c11705490..b53029ed7 100644 --- a/src/proof/README.md +++ b/src/proof/README.md @@ -1,3 +1,3 @@ # Proof of Existence -poe tutorial `=>` {ofc_workers, proof-based APIs} \ No newline at end of file +*[related issue (wip)](https://github.com/substrate-developer-hub/recipes/issues/47)* \ No newline at end of file From 61252fdf34ffd14886465fcccf87041411583b1a Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 30 Sep 2019 15:32:43 +0200 Subject: [PATCH 23/23] push to merge --- src/README.md | 18 ++++++++---------- src/SUMMARY.md | 6 +++--- src/basics/README.md | 2 +- src/dao/treasury.md | 7 ++----- src/declarative/README.md | 2 +- src/design/README.md | 4 +--- src/design/errtesting.md | 14 ++++---------- src/token/token.md | 4 +++- 8 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/README.md b/src/README.md index 9e0b02625..9d86ec4c2 100644 --- a/src/README.md +++ b/src/README.md @@ -8,28 +8,26 @@ - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - [Runtime Storage API](./storage/README.md) - - [Cache Multiple Calls](./storage/cache.md) + - [Cache Multiple Storage Calls](./storage/cache.md) - [Set Storage and Iteration](./storage/iterate.md) - [Lists: Maps vs Linked Maps](./storage/enumerated.md) - [Efficent Subgroup Removal by Subkey: Double Maps](./storage/double.md) - [Cheap Inclusion Proofs: Child Tries](./storage/childtries.md) - [Configurable Constants](./storage/constants.md) - [Best Practices](./design/README.md) - - [Types and Traits](./design/collateral.md) - - [Economic Security](./design/econ_cost.md) + - [Types and Traits](./design/ttraits.md) + - [Economic Security](./design/econsecurity.md) - [Runtime Errors and Testing](./design/errtesting.md) - [Condition Oriented Programming](./declarative/README.md) + - [Ensure Checks](./declarative/ensure.md) - [Fixed Point Arithmetic](./declarative/fpamath.md) - - [Verifying Signed Messages](./declarative/verify.md) - - [Check for Collisions](./declarative/collide.md) - - [Check for Overflow/Underflow](./declarative/overunder.md) - - [Manage State with Enums](./declarative/enums.md) + - [Verify Signed Messages; Custom Origin](./declarative/verify.md) - [Runtime Configuration](./base/runtime.md) -- [Simple Treasury](./dao/README.md) -- [Token Curated Registry](./token/README.md) +- [Simple Treasury](./dao/treasury.md) +- [Token Curated Registry](./token/token.md) - [NFT Ticket Machine](./nft/README.md) - [Proof of Existence](./proof/README.md) -- [Social Network](./identity/README.md) +- [Social Network](./identity/social.md) - [Featured Tutorials](./base/dessert.md) ----------- diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 6d7138f3a..9d86ec4c2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -4,7 +4,7 @@ - [Learn Rust](./base/rust.md) - [Installation](./base/setup.md) - [Hello Substrate](./basics/README.md) - - [Events Verify Execution](./basics/events) + - [Events Verify Execution](./basics/events.md) - [Adding Machine](./basics/adder.md) - [Single Value Storage](./basics/value.md) - [Runtime Storage API](./storage/README.md) @@ -15,8 +15,8 @@ - [Cheap Inclusion Proofs: Child Tries](./storage/childtries.md) - [Configurable Constants](./storage/constants.md) - [Best Practices](./design/README.md) - - [Types and Traits](./design/collateral.md) - - [Economic Security](./design/econ_cost.md) + - [Types and Traits](./design/ttraits.md) + - [Economic Security](./design/econsecurity.md) - [Runtime Errors and Testing](./design/errtesting.md) - [Condition Oriented Programming](./declarative/README.md) - [Ensure Checks](./declarative/ensure.md) diff --git a/src/basics/README.md b/src/basics/README.md index c2417a658..6f85b5470 100644 --- a/src/basics/README.md +++ b/src/basics/README.md @@ -24,7 +24,7 @@ decl_storage! { } ``` -Defined in [`decl_module`](https://crates.parity.io/srml_support/macro.decl_module.html), the runtime methods are specify acceptable interaction with runtime storage. +Defined in [`decl_module`](https://crates.parity.io/srml_support/macro.decl_module.html), the runtime methods specify acceptable interaction with runtime storage. ```rust decl_module! { diff --git a/src/dao/treasury.md b/src/dao/treasury.md index 24bb2ae2a..ad4ba4018 100644 --- a/src/dao/treasury.md +++ b/src/dao/treasury.md @@ -1,10 +1,7 @@ # smpl-treasury +*[`kitchen/modules/treasury`](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/modules/treasury)* -This recipe implements a basic version of the treasury. -* add permissioned section -* - -This recipe demonstrates how [`srml-treasury`](https://github.com/paritytech/substrate/blob/master/srml/treasury/src/lib.rs) instantiates a pot of funds and schedules funding. *See [kitchen/treasury](https://github.com/substrate-developer-hub/recipes/tree/master/kitchen/treasury) for the full code* +This recipe demonstrates how [`srml-treasury`](https://github.com/paritytech/substrate/blob/master/srml/treasury/src/lib.rs) instantiates a pot of funds and schedules funding. ## Instantiate a Pot diff --git a/src/declarative/README.md b/src/declarative/README.md index 56311492a..29ac64dd6 100644 --- a/src/declarative/README.md +++ b/src/declarative/README.md @@ -8,7 +8,7 @@ Each of the recipes in this section are oriented around increasing * [ensure checks](./ensure.md) * [fixed point arithmetic](./fpamath.md) * [verify signed messages](./verify.md) -* [checking for collisions](./collide.md) + ## Module Development Criteria diff --git a/src/design/README.md b/src/design/README.md index 56d05113f..94ddb8677 100644 --- a/src/design/README.md +++ b/src/design/README.md @@ -2,6 +2,4 @@ * [Types and Traits](./ttraits.md) * [Economic Security](./econsecurity.md) -* [Runtime Errors and Testing](./errtesting.md) - -Next Section: *[Condition-Oriented Programming](../declarative/README.md)* \ No newline at end of file +* [Runtime Errors and Testing](./errtesting.md) \ No newline at end of file diff --git a/src/design/errtesting.md b/src/design/errtesting.md index d106cde20..40749d2aa 100644 --- a/src/design/errtesting.md +++ b/src/design/errtesting.md @@ -25,13 +25,11 @@ impl module::Trait for Runtime { } ``` -Within the context of testing, there are a few ways of building a mock runtime that offer varying levels of customizations. The easiest way is to mock runtime storage with +Within the context of testing, there are a few ways of building a mock runtime that offer varying levels of customizations... -```rust - -``` +**This recipe is still in progress -- see [srml-balances](https://github.com/paritytech/substrate/tree/master/srml/balances/src) as well as the other [srml modules](https://github.com/paritytech/substrate/tree/master/srml) test scaffolding for more** -*To learn more about [test](), see the official docs* + \ No newline at end of file diff --git a/src/token/token.md b/src/token/token.md index 5fb09d674..e4623af5c 100644 --- a/src/token/token.md +++ b/src/token/token.md @@ -75,4 +75,6 @@ decl_module! { } ``` -S/O [`gautamdhameja/substrate-demo`](https://github.com/gautamdhameja/substrate-demo/blob/master/runtime/src/template.rs) for providing this recipe! \ No newline at end of file +S/O [`gautamdhameja/substrate-demo`](https://github.com/gautamdhameja/substrate-demo/blob/master/runtime/src/template.rs) for providing this recipe! + +*see the [TCR tutorial](https://github.com/substrate-developer-hub/substrate-tcr) for an extension in the form of a token curated registry* \ No newline at end of file