├── .editorconfig ├── .gitignore ├── .maintain └── frame-weight-template.hbs ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── docker-compose.yml ├── docs └── rust-setup.md ├── node ├── Cargo.toml ├── build.rs └── src │ ├── benchmarking.rs │ ├── chain_spec.rs │ ├── cli.rs │ ├── command.rs │ ├── main.rs │ ├── rpc.rs │ └── service.rs ├── pallets ├── coinflip │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── mock.rs │ │ └── tests.rs ├── data-type │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── genesis-config │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── ocw-signed │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── mock.rs │ │ └── tests.rs ├── ocw-unsigned │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── mock.rs │ │ └── tests.rs ├── poe │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── benchmarking.rs │ │ ├── lib.rs │ │ ├── mock.rs │ │ ├── tests.rs │ │ └── weights.rs ├── template │ ├── Cargo.toml │ ├── expanded.rs │ ├── expanded_pub.rs │ └── src │ │ ├── benchmarking.rs │ │ ├── lib.rs │ │ ├── mock.rs │ │ ├── tests.rs │ │ └── weights.rs └── weight │ ├── Cargo.toml │ └── src │ └── lib.rs ├── runtime ├── Cargo.toml ├── build.rs └── src │ └── lib.rs ├── rust-toolchain.toml ├── rustfmt.toml ├── scripts ├── docker_run.sh └── init.sh └── shell.nix /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style=space 5 | indent_size=2 6 | tab_width=2 7 | end_of_line=lf 8 | charset=utf-8 9 | trim_trailing_whitespace=true 10 | insert_final_newline = true 11 | 12 | [*.{rs,toml}] 13 | indent_style=tab 14 | indent_size=tab 15 | tab_width=4 16 | max_line_length=100 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | # These are backup files generated by rustfmt 5 | **/*.rs.bk 6 | 7 | .DS_Store 8 | 9 | # The cache for docker container dependency 10 | .cargo 11 | 12 | # The cache for chain data in container 13 | .local 14 | 15 | # Temp files for local development 16 | maintain 17 | 18 | raw.json 19 | -------------------------------------------------------------------------------- /.maintain/frame-weight-template.hbs: -------------------------------------------------------------------------------- 1 | {{header}} 2 | //! Autogenerated weights for {{pallet}} 3 | //! 4 | //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} 5 | //! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` 6 | //! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` 7 | //! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}` 8 | //! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} 9 | 10 | // Executed Command: 11 | {{#each args as |arg|}} 12 | // {{arg}} 13 | {{/each}} 14 | 15 | #![cfg_attr(rustfmt, rustfmt_skip)] 16 | #![allow(unused_parens)] 17 | #![allow(unused_imports)] 18 | 19 | use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; 20 | use sp_std::marker::PhantomData; 21 | 22 | /// Weight functions needed for {{pallet}}. 23 | pub trait WeightInfo { 24 | {{#each benchmarks as |benchmark|}} 25 | fn {{benchmark.name~}} 26 | ( 27 | {{~#each benchmark.components as |c| ~}} 28 | {{c.name}}: u32, {{/each~}} 29 | ) -> Weight; 30 | {{/each}} 31 | } 32 | 33 | /// Weights for {{pallet}} using the Substrate node and recommended hardware. 34 | pub struct SubstrateWeight(PhantomData); 35 | {{#if (eq pallet "frame_system")}} 36 | impl WeightInfo for SubstrateWeight { 37 | {{else}} 38 | impl WeightInfo for SubstrateWeight { 39 | {{/if}} 40 | {{#each benchmarks as |benchmark|}} 41 | {{#each benchmark.comments as |comment|}} 42 | /// {{comment}} 43 | {{/each}} 44 | {{#each benchmark.component_ranges as |range|}} 45 | /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. 46 | {{/each}} 47 | fn {{benchmark.name~}} 48 | ( 49 | {{~#each benchmark.components as |c| ~}} 50 | {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} 51 | ) -> Weight { 52 | // Proof Size summary in bytes: 53 | // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` 54 | // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` 55 | // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. 56 | Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) 57 | {{#each benchmark.component_weight as |cw|}} 58 | // Standard Error: {{underscore cw.error}} 59 | .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) 60 | {{/each}} 61 | {{#if (ne benchmark.base_reads "0")}} 62 | .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) 63 | {{/if}} 64 | {{#each benchmark.component_reads as |cr|}} 65 | .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) 66 | {{/each}} 67 | {{#if (ne benchmark.base_writes "0")}} 68 | .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) 69 | {{/if}} 70 | {{#each benchmark.component_writes as |cw|}} 71 | .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) 72 | {{/each}} 73 | {{#each benchmark.component_calculated_proof_size as |cp|}} 74 | .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) 75 | {{/each}} 76 | } 77 | {{/each}} 78 | } 79 | 80 | // For backwards compatibility and tests 81 | impl WeightInfo for () { 82 | {{#each benchmarks as |benchmark|}} 83 | {{#each benchmark.comments as |comment|}} 84 | /// {{comment}} 85 | {{/each}} 86 | {{#each benchmark.component_ranges as |range|}} 87 | /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. 88 | {{/each}} 89 | fn {{benchmark.name~}} 90 | ( 91 | {{~#each benchmark.components as |c| ~}} 92 | {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} 93 | ) -> Weight { 94 | // Proof Size summary in bytes: 95 | // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` 96 | // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` 97 | // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. 98 | Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) 99 | {{#each benchmark.component_weight as |cw|}} 100 | // Standard Error: {{underscore cw.error}} 101 | .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) 102 | {{/each}} 103 | {{#if (ne benchmark.base_reads "0")}} 104 | .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) 105 | {{/if}} 106 | {{#each benchmark.component_reads as |cr|}} 107 | .saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) 108 | {{/each}} 109 | {{#if (ne benchmark.base_writes "0")}} 110 | .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64)) 111 | {{/if}} 112 | {{#each benchmark.component_writes as |cw|}} 113 | .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) 114 | {{/each}} 115 | {{#each benchmark.component_calculated_proof_size as |cp|}} 116 | .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) 117 | {{/each}} 118 | } 119 | {{/each}} 120 | } 121 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | 'node', 4 | 'pallets/template', 5 | 'pallets/poe', 6 | # 'pallets/data-type', 7 | # 'pallets/genesis-config', 8 | 'runtime', 9 | ] 10 | [profile.release] 11 | panic = 'unwind' 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Play Substrate 2 | 3 | A few simple pallets for learning and demo the capability of Substrate blockchain framework. 4 | 5 | ## Runtime Modules 6 | 7 | Disclaimer: these modules are for learning purpose, you should never use them in *production*. 8 | 9 | - **Template Pallet**, A simple module to experience simple storage data type, error handling, test and benchmark structure. 10 | - **Proof of Existence Pallet**, A simple module to store a claim (BoundedVec\), revoke it, and transfer the claim to someone else. This pallet includes, 11 | - busisness logic to create/revoke/transfer a claim. 12 | - unit test of logic 13 | - benchmark of weights used for each dispatchables. 14 | - **Data Type Pallet**, It helps with better understanding what Rust types are supported in Substrate blockchain development. 15 | - **Coin Flip Game Pallet**, *Outdated* 16 | - **Benchmark Demo**, *Deprecated*, you can find similar code in Proof of Existence pallet. 17 | - **Offchain Worker** 18 | - Offchain worker - send unsigned transaction (outdated) 19 | - Offchain worker - send signed transaction (outdated) 20 | - **Genesis Config Pallet**, *Outdated* 21 | - **Custome Weight**, *Outdated* 22 | 23 | ## Getting Started 24 | 25 | ### Install Dependencies 26 | 27 | Before you start developing with Substrate, you need to prepare your environment by following the [doc](https://docs.substrate.io/main-docs/install/). 28 | 29 | ### Compile and Run 30 | 31 | ```shell 32 | # compile the code into binary 33 | cargo build --release 34 | 35 | # start the compiled binary with local dev network 36 | ./target/release/node-template --dev 37 | ``` 38 | ### Connect with Front-end 39 | 40 | Once the node template is running locally, you can connect it with **Polkadot-JS Apps** front-end 41 | to interact with your chain. [Click 42 | here](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944) connecting the Apps to your 43 | local node template. 44 | 45 | ### Test the code 46 | 47 | ```shell 48 | # test only one pallet 49 | cargo test -p pallet-poe 50 | 51 | # test all the pallets 52 | cargo test 53 | ``` 54 | 55 | ### Benchmark 56 | 57 | Compile the node with `runtime-benchmarks` feature, 58 | ```shell 59 | cargo build --release --features runtime-benchmarks 60 | ``` 61 | 62 | Run the benchmark for Proof of Existence pallet, 63 | ```shell 64 | ./target/release/node-template benchmark pallet --chain dev --execution wasm --wasm-execution compiled --pallet pallet_poe --extrinsic "*" --steps 20 --repeat 10 --output ./pallets/poe/src/weights.rs --template .maintain/frame-weight-template.hbs 65 | ``` 66 | 67 | Show all the available benchmarks, 68 | ```shell 69 | ./target/release/node-template benchmark pallet --chain dev --pallet "*" --extrinsic "*" --repeat 0 70 | ``` 71 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.2" 2 | 3 | services: 4 | dev: 5 | container_name: node-template 6 | image: paritytech/ci-linux:974ba3ac-20201006 7 | working_dir: /var/www/node-template 8 | ports: 9 | - "9944:9944" 10 | environment: 11 | - CARGO_HOME=/var/www/node-template/.cargo 12 | volumes: 13 | - .:/var/www/node-template 14 | - type: bind 15 | source: ./.local 16 | target: /root/.local 17 | command: bash -c "cargo build --release && ./target/release/node-template --dev --ws-external" 18 | -------------------------------------------------------------------------------- /docs/rust-setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | --- 4 | 5 | This page will guide you through the steps needed to prepare a computer for development with the 6 | Substrate Node Template. Since Substrate is built with 7 | [the Rust programming language](https://www.rust-lang.org/), the first thing you will need to do is 8 | prepare the computer for Rust development - these steps will vary based on the computer's operating 9 | system. Once Rust is configured, you will use its toolchains to interact with Rust projects; the 10 | commands for Rust's toolchains will be the same for all supported, Unix-based operating systems. 11 | 12 | ## Unix-Based Operating Systems 13 | 14 | Substrate development is easiest on Unix-based operating systems like macOS or Linux. The examples 15 | in the Substrate [Tutorials](https://docs.substrate.io/tutorials/v3) and 16 | [How-to Guides](https://docs.substrate.io/how-to-guides/v3) use Unix-style terminals to demonstrate 17 | how to interact with Substrate from the command line. 18 | 19 | ### macOS 20 | 21 | Open the Terminal application and execute the following commands: 22 | 23 | ```bash 24 | # Install Homebrew if necessary https://brew.sh/ 25 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" 26 | 27 | # Make sure Homebrew is up-to-date, install openssl and cmake 28 | brew update 29 | brew install openssl cmake 30 | ``` 31 | 32 | ### Ubuntu/Debian 33 | 34 | Use a terminal shell to execute the following commands: 35 | 36 | ```bash 37 | sudo apt update 38 | # May prompt for location information 39 | sudo apt install -y cmake pkg-config libssl-dev git build-essential clang libclang-dev curl 40 | ``` 41 | 42 | ### Arch Linux 43 | 44 | Run these commands from a terminal: 45 | 46 | ```bash 47 | pacman -Syu --needed --noconfirm cmake gcc openssl-1.0 pkgconf git clang 48 | export OPENSSL_LIB_DIR="/usr/lib/openssl-1.0" 49 | export OPENSSL_INCLUDE_DIR="/usr/include/openssl-1.0" 50 | ``` 51 | 52 | ### Fedora/RHEL/CentOS 53 | 54 | Use a terminal to run the following commands: 55 | 56 | ```bash 57 | # Update 58 | sudo dnf update 59 | # Install packages 60 | sudo dnf install cmake pkgconfig rocksdb rocksdb-devel llvm git libcurl libcurl-devel curl-devel clang 61 | ``` 62 | 63 | ## Rust Developer Environment 64 | 65 | This project uses [`rustup`](https://rustup.rs/) to help manage the Rust toolchain. First install 66 | and configure `rustup`: 67 | 68 | ```bash 69 | # Install 70 | curl https://sh.rustup.rs -sSf | sh 71 | # Configure 72 | source ~/.cargo/env 73 | ``` 74 | 75 | Finally, configure the Rust toolchain: 76 | 77 | ```bash 78 | rustup default stable 79 | rustup update nightly 80 | rustup update stable 81 | rustup target add wasm32-unknown-unknown --toolchain nightly 82 | ``` 83 | -------------------------------------------------------------------------------- /node/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "node-template" 3 | version = "4.0.0-dev" 4 | description = "A fresh FRAME-based Substrate node, ready for hacking." 5 | authors = ["Substrate DevHub "] 6 | homepage = "https://substrate.io/" 7 | edition = "2021" 8 | license = "Unlicense" 9 | publish = false 10 | repository = "https://github.com/substrate-developer-hub/substrate-node-template/" 11 | build = "build.rs" 12 | 13 | [package.metadata.docs.rs] 14 | targets = ["x86_64-unknown-linux-gnu"] 15 | 16 | [[bin]] 17 | name = "node-template" 18 | 19 | [dependencies] 20 | clap = { version = "4.5.1", features = ["derive"] } 21 | futures = { version = "0.3.21", features = ["thread-pool"]} 22 | serde_json = { version = "1.0.114", default-features = true } 23 | 24 | sc-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 25 | sp-core = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 26 | sc-executor = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 27 | sc-service = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 28 | sc-telemetry = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 29 | sc-keystore = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 30 | sc-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 31 | sc-transaction-pool-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 32 | sc-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 33 | sp-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 34 | sp-consensus = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 35 | sc-consensus = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 36 | sc-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 37 | sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 38 | sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 39 | sc-offchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 40 | sc-network = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 41 | sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 42 | sp-io = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 43 | sp-timestamp = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 44 | sp-inherents = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 45 | sp-keyring = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 46 | frame-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 47 | pallet-transaction-payment = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 48 | 49 | # These dependencies are used for the node template's RPCs 50 | jsonrpsee = { version = "0.22", features = ["server"] } 51 | sp-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 52 | sc-rpc-api = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 53 | sp-blockchain = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 54 | sp-block-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 55 | sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 56 | substrate-frame-rpc-system = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 57 | pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 58 | 59 | # These dependencies are used for runtime benchmarking 60 | frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 61 | frame-benchmarking-cli = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 62 | 63 | # Local Dependencies 64 | node-template-runtime = { path = "../runtime" } 65 | 66 | # CLI-specific dependencies 67 | try-runtime-cli = { optional = true, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 68 | 69 | [build-dependencies] 70 | substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 71 | 72 | [features] 73 | default = [] 74 | # Dependencies that are only required if runtime benchmarking should be build. 75 | runtime-benchmarks = [ 76 | "node-template-runtime/runtime-benchmarks", 77 | "frame-benchmarking/runtime-benchmarks", 78 | "frame-benchmarking-cli/runtime-benchmarks", 79 | ] 80 | # Enable features that allow the runtime to be tried and debugged. Name might be subject to change 81 | # in the near future. 82 | try-runtime = ["node-template-runtime/try-runtime", "try-runtime-cli/try-runtime"] 83 | -------------------------------------------------------------------------------- /node/build.rs: -------------------------------------------------------------------------------- 1 | use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; 2 | 3 | fn main() { 4 | generate_cargo_keys(); 5 | 6 | rerun_if_git_head_changed(); 7 | } 8 | -------------------------------------------------------------------------------- /node/src/benchmarking.rs: -------------------------------------------------------------------------------- 1 | //! Setup code for [`super::command`] which would otherwise bloat that module. 2 | //! 3 | //! Should only be used for benchmarking as it may break in other contexts. 4 | 5 | use crate::service::FullClient; 6 | 7 | use node_template_runtime as runtime; 8 | use runtime::{AccountId, Balance, BalancesCall, SystemCall}; 9 | use sc_cli::Result; 10 | use sc_client_api::BlockBackend; 11 | use sp_core::{Encode, Pair}; 12 | use sp_inherents::{InherentData, InherentDataProvider}; 13 | use sp_keyring::Sr25519Keyring; 14 | use sp_runtime::{OpaqueExtrinsic, SaturatedConversion}; 15 | 16 | use std::{sync::Arc, time::Duration}; 17 | 18 | /// Generates extrinsics for the `benchmark overhead` command. 19 | /// 20 | /// Note: Should only be used for benchmarking. 21 | pub struct RemarkBuilder { 22 | client: Arc, 23 | } 24 | 25 | impl RemarkBuilder { 26 | /// Creates a new [`Self`] from the given client. 27 | pub fn new(client: Arc) -> Self { 28 | Self { client } 29 | } 30 | } 31 | 32 | impl frame_benchmarking_cli::ExtrinsicBuilder for RemarkBuilder { 33 | fn pallet(&self) -> &str { 34 | "system" 35 | } 36 | 37 | fn extrinsic(&self) -> &str { 38 | "remark" 39 | } 40 | 41 | fn build(&self, nonce: u32) -> std::result::Result { 42 | let acc = Sr25519Keyring::Bob.pair(); 43 | let extrinsic: OpaqueExtrinsic = create_benchmark_extrinsic( 44 | self.client.as_ref(), 45 | acc, 46 | SystemCall::remark { remark: vec![] }.into(), 47 | nonce, 48 | ) 49 | .into(); 50 | 51 | Ok(extrinsic) 52 | } 53 | } 54 | 55 | /// Generates `Balances::TransferKeepAlive` extrinsics for the benchmarks. 56 | /// 57 | /// Note: Should only be used for benchmarking. 58 | pub struct TransferKeepAliveBuilder { 59 | client: Arc, 60 | dest: AccountId, 61 | value: Balance, 62 | } 63 | 64 | impl TransferKeepAliveBuilder { 65 | /// Creates a new [`Self`] from the given client. 66 | pub fn new(client: Arc, dest: AccountId, value: Balance) -> Self { 67 | Self { client, dest, value } 68 | } 69 | } 70 | 71 | impl frame_benchmarking_cli::ExtrinsicBuilder for TransferKeepAliveBuilder { 72 | fn pallet(&self) -> &str { 73 | "balances" 74 | } 75 | 76 | fn extrinsic(&self) -> &str { 77 | "transfer_keep_alive" 78 | } 79 | 80 | fn build(&self, nonce: u32) -> std::result::Result { 81 | let acc = Sr25519Keyring::Bob.pair(); 82 | let extrinsic: OpaqueExtrinsic = create_benchmark_extrinsic( 83 | self.client.as_ref(), 84 | acc, 85 | BalancesCall::transfer_keep_alive { dest: self.dest.clone().into(), value: self.value } 86 | .into(), 87 | nonce, 88 | ) 89 | .into(); 90 | 91 | Ok(extrinsic) 92 | } 93 | } 94 | 95 | /// Create a transaction using the given `call`. 96 | /// 97 | /// Note: Should only be used for benchmarking. 98 | pub fn create_benchmark_extrinsic( 99 | client: &FullClient, 100 | sender: sp_core::sr25519::Pair, 101 | call: runtime::RuntimeCall, 102 | nonce: u32, 103 | ) -> runtime::UncheckedExtrinsic { 104 | let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); 105 | let best_hash = client.chain_info().best_hash; 106 | let best_block = client.chain_info().best_number; 107 | 108 | let period = runtime::BlockHashCount::get() 109 | .checked_next_power_of_two() 110 | .map(|c| c / 2) 111 | .unwrap_or(2) as u64; 112 | let extra: runtime::SignedExtra = ( 113 | frame_system::CheckNonZeroSender::::new(), 114 | frame_system::CheckSpecVersion::::new(), 115 | frame_system::CheckTxVersion::::new(), 116 | frame_system::CheckGenesis::::new(), 117 | frame_system::CheckEra::::from(sp_runtime::generic::Era::mortal( 118 | period, 119 | best_block.saturated_into(), 120 | )), 121 | frame_system::CheckNonce::::from(nonce), 122 | frame_system::CheckWeight::::new(), 123 | pallet_transaction_payment::ChargeTransactionPayment::::from(0), 124 | ); 125 | 126 | let raw_payload = runtime::SignedPayload::from_raw( 127 | call.clone(), 128 | extra.clone(), 129 | ( 130 | (), 131 | runtime::VERSION.spec_version, 132 | runtime::VERSION.transaction_version, 133 | genesis_hash, 134 | best_hash, 135 | (), 136 | (), 137 | (), 138 | ), 139 | ); 140 | let signature = raw_payload.using_encoded(|e| sender.sign(e)); 141 | 142 | runtime::UncheckedExtrinsic::new_signed( 143 | call, 144 | sp_runtime::AccountId32::from(sender.public()).into(), 145 | runtime::Signature::Sr25519(signature), 146 | extra, 147 | ) 148 | } 149 | 150 | /// Generates inherent data for the `benchmark overhead` command. 151 | /// 152 | /// Note: Should only be used for benchmarking. 153 | pub fn inherent_benchmark_data() -> Result { 154 | let mut inherent_data = InherentData::new(); 155 | let d = Duration::from_millis(0); 156 | let timestamp = sp_timestamp::InherentDataProvider::new(d.into()); 157 | 158 | futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data)) 159 | .map_err(|e| format!("creating inherent data: {:?}", e))?; 160 | Ok(inherent_data) 161 | } 162 | -------------------------------------------------------------------------------- /node/src/chain_spec.rs: -------------------------------------------------------------------------------- 1 | use node_template_runtime::{AccountId, RuntimeGenesisConfig, Signature, WASM_BINARY}; 2 | use sc_service::ChainType; 3 | use sp_consensus_aura::sr25519::AuthorityId as AuraId; 4 | use sp_consensus_grandpa::AuthorityId as GrandpaId; 5 | use sp_core::{sr25519, Pair, Public}; 6 | use sp_runtime::traits::{IdentifyAccount, Verify}; 7 | 8 | // The URL for the telemetry server. 9 | // const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; 10 | 11 | /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. 12 | pub type ChainSpec = sc_service::GenericChainSpec; 13 | 14 | /// Generate a crypto pair from seed. 15 | pub fn get_from_seed(seed: &str) -> ::Public { 16 | TPublic::Pair::from_string(&format!("//{}", seed), None) 17 | .expect("static values are valid; qed") 18 | .public() 19 | } 20 | 21 | type AccountPublic = ::Signer; 22 | 23 | /// Generate an account ID from seed. 24 | pub fn get_account_id_from_seed(seed: &str) -> AccountId 25 | where 26 | AccountPublic: From<::Public>, 27 | { 28 | AccountPublic::from(get_from_seed::(seed)).into_account() 29 | } 30 | 31 | /// Generate an Aura authority key. 32 | pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { 33 | (get_from_seed::(s), get_from_seed::(s)) 34 | } 35 | 36 | pub fn development_config() -> Result { 37 | Ok(ChainSpec::builder( 38 | WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, 39 | None, 40 | ) 41 | .with_name("Development") 42 | .with_id("dev") 43 | .with_chain_type(ChainType::Development) 44 | .with_genesis_config_patch(testnet_genesis( 45 | // Initial PoA authorities 46 | vec![authority_keys_from_seed("Alice")], 47 | // Sudo account 48 | get_account_id_from_seed::("Alice"), 49 | // Pre-funded accounts 50 | vec![ 51 | get_account_id_from_seed::("Alice"), 52 | get_account_id_from_seed::("Bob"), 53 | get_account_id_from_seed::("Alice//stash"), 54 | get_account_id_from_seed::("Bob//stash"), 55 | ], 56 | true, 57 | )) 58 | .build()) 59 | } 60 | 61 | pub fn local_testnet_config() -> Result { 62 | Ok(ChainSpec::builder( 63 | WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, 64 | None, 65 | ) 66 | .with_name("Local Testnet") 67 | .with_id("local_testnet") 68 | .with_chain_type(ChainType::Local) 69 | .with_genesis_config_patch(testnet_genesis( 70 | // Initial PoA authorities 71 | vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")], 72 | // Sudo account 73 | get_account_id_from_seed::("Alice"), 74 | // Pre-funded accounts 75 | vec![ 76 | get_account_id_from_seed::("Alice"), 77 | get_account_id_from_seed::("Bob"), 78 | get_account_id_from_seed::("Charlie"), 79 | get_account_id_from_seed::("Dave"), 80 | get_account_id_from_seed::("Eve"), 81 | get_account_id_from_seed::("Ferdie"), 82 | get_account_id_from_seed::("Alice//stash"), 83 | get_account_id_from_seed::("Bob//stash"), 84 | get_account_id_from_seed::("Charlie//stash"), 85 | get_account_id_from_seed::("Dave//stash"), 86 | get_account_id_from_seed::("Eve//stash"), 87 | get_account_id_from_seed::("Ferdie//stash"), 88 | ], 89 | true, 90 | )) 91 | .build()) 92 | } 93 | 94 | /// Configure initial storage state for FRAME modules. 95 | fn testnet_genesis( 96 | initial_authorities: Vec<(AuraId, GrandpaId)>, 97 | root_key: AccountId, 98 | endowed_accounts: Vec, 99 | _enable_println: bool, 100 | ) -> serde_json::Value { 101 | serde_json::json!({ 102 | "balances": { 103 | // Configure endowed accounts with initial balance of 1 << 60. 104 | "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), 105 | }, 106 | "aura": { 107 | "authorities": initial_authorities.iter().map(|x| (x.0.clone())).collect::>(), 108 | }, 109 | "grandpa": { 110 | "authorities": initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::>(), 111 | }, 112 | "sudo": { 113 | // Assign network admin rights. 114 | "key": Some(root_key), 115 | }, 116 | }) 117 | } 118 | -------------------------------------------------------------------------------- /node/src/cli.rs: -------------------------------------------------------------------------------- 1 | use sc_cli::RunCmd; 2 | 3 | #[derive(Debug, clap::Parser)] 4 | pub struct Cli { 5 | #[command(subcommand)] 6 | pub subcommand: Option, 7 | 8 | #[clap(flatten)] 9 | pub run: RunCmd, 10 | } 11 | 12 | #[derive(Debug, clap::Subcommand)] 13 | #[allow(clippy::large_enum_variant)] 14 | pub enum Subcommand { 15 | /// Key management cli utilities 16 | #[command(subcommand)] 17 | Key(sc_cli::KeySubcommand), 18 | 19 | /// Build a chain specification. 20 | BuildSpec(sc_cli::BuildSpecCmd), 21 | 22 | /// Validate blocks. 23 | CheckBlock(sc_cli::CheckBlockCmd), 24 | 25 | /// Export blocks. 26 | ExportBlocks(sc_cli::ExportBlocksCmd), 27 | 28 | /// Export the state of a given block into a chain spec. 29 | ExportState(sc_cli::ExportStateCmd), 30 | 31 | /// Import blocks. 32 | ImportBlocks(sc_cli::ImportBlocksCmd), 33 | 34 | /// Remove the whole chain. 35 | PurgeChain(sc_cli::PurgeChainCmd), 36 | 37 | /// Revert the chain to a previous state. 38 | Revert(sc_cli::RevertCmd), 39 | 40 | /// Sub-commands concerned with benchmarking. 41 | #[command(subcommand)] 42 | Benchmark(frame_benchmarking_cli::BenchmarkCmd), 43 | 44 | /// Try-runtime has migrated to a standalone CLI 45 | /// (). The subcommand exists as a stub and 46 | /// deprecation notice. It will be removed entirely some time after Janurary 2024. 47 | TryRuntime, 48 | 49 | /// Db meta columns information. 50 | ChainInfo(sc_cli::ChainInfoCmd), 51 | } 52 | -------------------------------------------------------------------------------- /node/src/command.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | benchmarking::{inherent_benchmark_data, RemarkBuilder, TransferKeepAliveBuilder}, 3 | chain_spec, 4 | cli::{Cli, Subcommand}, 5 | service, 6 | }; 7 | use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; 8 | use node_template_runtime::{Block, EXISTENTIAL_DEPOSIT}; 9 | use sc_cli::SubstrateCli; 10 | use sc_service::PartialComponents; 11 | use sp_keyring::Sr25519Keyring; 12 | 13 | impl SubstrateCli for Cli { 14 | fn impl_name() -> String { 15 | "Substrate Node".into() 16 | } 17 | 18 | fn impl_version() -> String { 19 | env!("SUBSTRATE_CLI_IMPL_VERSION").into() 20 | } 21 | 22 | fn description() -> String { 23 | env!("CARGO_PKG_DESCRIPTION").into() 24 | } 25 | 26 | fn author() -> String { 27 | env!("CARGO_PKG_AUTHORS").into() 28 | } 29 | 30 | fn support_url() -> String { 31 | "support.anonymous.an".into() 32 | } 33 | 34 | fn copyright_start_year() -> i32 { 35 | 2017 36 | } 37 | 38 | fn load_spec(&self, id: &str) -> Result, String> { 39 | Ok(match id { 40 | "dev" => Box::new(chain_spec::development_config()?), 41 | "" | "local" => Box::new(chain_spec::local_testnet_config()?), 42 | path => 43 | Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), 44 | }) 45 | } 46 | } 47 | 48 | /// Parse and run command line arguments 49 | pub fn run() -> sc_cli::Result<()> { 50 | let cli = Cli::from_args(); 51 | 52 | match &cli.subcommand { 53 | Some(Subcommand::Key(cmd)) => cmd.run(&cli), 54 | Some(Subcommand::BuildSpec(cmd)) => { 55 | let runner = cli.create_runner(cmd)?; 56 | runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) 57 | }, 58 | Some(Subcommand::CheckBlock(cmd)) => { 59 | let runner = cli.create_runner(cmd)?; 60 | runner.async_run(|config| { 61 | let PartialComponents { client, task_manager, import_queue, .. } = 62 | service::new_partial(&config)?; 63 | Ok((cmd.run(client, import_queue), task_manager)) 64 | }) 65 | }, 66 | Some(Subcommand::ExportBlocks(cmd)) => { 67 | let runner = cli.create_runner(cmd)?; 68 | runner.async_run(|config| { 69 | let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?; 70 | Ok((cmd.run(client, config.database), task_manager)) 71 | }) 72 | }, 73 | Some(Subcommand::ExportState(cmd)) => { 74 | let runner = cli.create_runner(cmd)?; 75 | runner.async_run(|config| { 76 | let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?; 77 | Ok((cmd.run(client, config.chain_spec), task_manager)) 78 | }) 79 | }, 80 | Some(Subcommand::ImportBlocks(cmd)) => { 81 | let runner = cli.create_runner(cmd)?; 82 | runner.async_run(|config| { 83 | let PartialComponents { client, task_manager, import_queue, .. } = 84 | service::new_partial(&config)?; 85 | Ok((cmd.run(client, import_queue), task_manager)) 86 | }) 87 | }, 88 | Some(Subcommand::PurgeChain(cmd)) => { 89 | let runner = cli.create_runner(cmd)?; 90 | runner.sync_run(|config| cmd.run(config.database)) 91 | }, 92 | Some(Subcommand::Revert(cmd)) => { 93 | let runner = cli.create_runner(cmd)?; 94 | runner.async_run(|config| { 95 | let PartialComponents { client, task_manager, backend, .. } = 96 | service::new_partial(&config)?; 97 | let aux_revert = Box::new(|client, _, blocks| { 98 | sc_consensus_grandpa::revert(client, blocks)?; 99 | Ok(()) 100 | }); 101 | Ok((cmd.run(client, backend, Some(aux_revert)), task_manager)) 102 | }) 103 | }, 104 | Some(Subcommand::Benchmark(cmd)) => { 105 | let runner = cli.create_runner(cmd)?; 106 | 107 | runner.sync_run(|config| { 108 | // This switch needs to be in the client, since the client decides 109 | // which sub-commands it wants to support. 110 | match cmd { 111 | BenchmarkCmd::Pallet(cmd) => { 112 | if !cfg!(feature = "runtime-benchmarks") { 113 | return Err( 114 | "Runtime benchmarking wasn't enabled when building the node. \ 115 | You can enable it with `--features runtime-benchmarks`." 116 | .into(), 117 | ) 118 | } 119 | 120 | cmd.run::, ()>(config) 121 | }, 122 | BenchmarkCmd::Block(cmd) => { 123 | let PartialComponents { client, .. } = service::new_partial(&config)?; 124 | cmd.run(client) 125 | }, 126 | #[cfg(not(feature = "runtime-benchmarks"))] 127 | BenchmarkCmd::Storage(_) => Err( 128 | "Storage benchmarking can be enabled with `--features runtime-benchmarks`." 129 | .into(), 130 | ), 131 | #[cfg(feature = "runtime-benchmarks")] 132 | BenchmarkCmd::Storage(cmd) => { 133 | let PartialComponents { client, backend, .. } = 134 | service::new_partial(&config)?; 135 | let db = backend.expose_db(); 136 | let storage = backend.expose_storage(); 137 | 138 | cmd.run(config, client, db, storage) 139 | }, 140 | BenchmarkCmd::Overhead(cmd) => { 141 | let PartialComponents { client, .. } = service::new_partial(&config)?; 142 | let ext_builder = RemarkBuilder::new(client.clone()); 143 | 144 | cmd.run( 145 | config, 146 | client, 147 | inherent_benchmark_data()?, 148 | Vec::new(), 149 | &ext_builder, 150 | ) 151 | }, 152 | BenchmarkCmd::Extrinsic(cmd) => { 153 | let PartialComponents { client, .. } = service::new_partial(&config)?; 154 | // Register the *Remark* and *TKA* builders. 155 | let ext_factory = ExtrinsicFactory(vec![ 156 | Box::new(RemarkBuilder::new(client.clone())), 157 | Box::new(TransferKeepAliveBuilder::new( 158 | client.clone(), 159 | Sr25519Keyring::Alice.to_account_id(), 160 | EXISTENTIAL_DEPOSIT, 161 | )), 162 | ]); 163 | 164 | cmd.run(client, inherent_benchmark_data()?, Vec::new(), &ext_factory) 165 | }, 166 | BenchmarkCmd::Machine(cmd) => 167 | cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()), 168 | } 169 | }) 170 | }, 171 | #[cfg(feature = "try-runtime")] 172 | Some(Subcommand::TryRuntime) => Err(try_runtime_cli::DEPRECATION_NOTICE.into()), 173 | #[cfg(not(feature = "try-runtime"))] 174 | Some(Subcommand::TryRuntime) => Err("TryRuntime wasn't enabled when building the node. \ 175 | You can enable it with `--features try-runtime`." 176 | .into()), 177 | Some(Subcommand::ChainInfo(cmd)) => { 178 | let runner = cli.create_runner(cmd)?; 179 | runner.sync_run(|config| cmd.run::(&config)) 180 | }, 181 | None => { 182 | let runner = cli.create_runner(&cli.run)?; 183 | runner.run_node_until_exit(|config| async move { 184 | service::new_full(config).map_err(sc_cli::Error::Service) 185 | }) 186 | }, 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /node/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Substrate Node Template CLI library. 2 | #![warn(missing_docs)] 3 | 4 | mod benchmarking; 5 | mod chain_spec; 6 | mod cli; 7 | mod command; 8 | mod rpc; 9 | mod service; 10 | 11 | fn main() -> sc_cli::Result<()> { 12 | command::run() 13 | } 14 | -------------------------------------------------------------------------------- /node/src/rpc.rs: -------------------------------------------------------------------------------- 1 | //! A collection of node-specific RPC methods. 2 | //! Substrate provides the `sc-rpc` crate, which defines the core RPC layer 3 | //! used by Substrate nodes. This file extends those RPC definitions with 4 | //! capabilities that are specific to this project's runtime configuration. 5 | 6 | #![warn(missing_docs)] 7 | 8 | use std::sync::Arc; 9 | 10 | use jsonrpsee::RpcModule; 11 | use node_template_runtime::{opaque::Block, AccountId, Balance, Nonce}; 12 | use sc_transaction_pool_api::TransactionPool; 13 | use sp_api::ProvideRuntimeApi; 14 | use sp_block_builder::BlockBuilder; 15 | use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; 16 | 17 | pub use sc_rpc_api::DenyUnsafe; 18 | 19 | /// Full client dependencies. 20 | pub struct FullDeps { 21 | /// The client instance to use. 22 | pub client: Arc, 23 | /// Transaction pool instance. 24 | pub pool: Arc

, 25 | /// Whether to deny unsafe calls 26 | pub deny_unsafe: DenyUnsafe, 27 | } 28 | 29 | /// Instantiate all full RPC extensions. 30 | pub fn create_full( 31 | deps: FullDeps, 32 | ) -> Result, Box> 33 | where 34 | C: ProvideRuntimeApi, 35 | C: HeaderBackend + HeaderMetadata + 'static, 36 | C: Send + Sync + 'static, 37 | C::Api: substrate_frame_rpc_system::AccountNonceApi, 38 | C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, 39 | C::Api: BlockBuilder, 40 | P: TransactionPool + 'static, 41 | { 42 | use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; 43 | use substrate_frame_rpc_system::{System, SystemApiServer}; 44 | 45 | let mut module = RpcModule::new(()); 46 | let FullDeps { client, pool, deny_unsafe } = deps; 47 | 48 | module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; 49 | module.merge(TransactionPayment::new(client).into_rpc())?; 50 | 51 | // Extend this RPC with a custom API by using the following syntax. 52 | // `YourRpcStruct` should have a reference to a client, which is needed 53 | // to call into the runtime. 54 | // `module.merge(YourRpcTrait::into_rpc(YourRpcStruct::new(ReferenceToClient, ...)))?;` 55 | 56 | // You probably want to enable the `rpc v2 chainSpec` API as well 57 | // 58 | // let chain_name = chain_spec.name().to_string(); 59 | // let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); 60 | // let properties = chain_spec.properties(); 61 | // module.merge(ChainSpec::new(chain_name, genesis_hash, properties).into_rpc())?; 62 | 63 | Ok(module) 64 | } 65 | -------------------------------------------------------------------------------- /node/src/service.rs: -------------------------------------------------------------------------------- 1 | //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. 2 | 3 | use futures::FutureExt; 4 | use node_template_runtime::{self, opaque::Block, RuntimeApi}; 5 | use sc_client_api::{Backend, BlockBackend}; 6 | use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; 7 | use sc_consensus_grandpa::SharedVoterState; 8 | use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncParams}; 9 | use sc_telemetry::{Telemetry, TelemetryWorker}; 10 | use sc_transaction_pool_api::OffchainTransactionPoolFactory; 11 | use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; 12 | use std::{sync::Arc, time::Duration}; 13 | 14 | pub(crate) type FullClient = sc_service::TFullClient< 15 | Block, 16 | RuntimeApi, 17 | sc_executor::WasmExecutor, 18 | >; 19 | type FullBackend = sc_service::TFullBackend; 20 | type FullSelectChain = sc_consensus::LongestChain; 21 | 22 | /// The minimum period of blocks on which justifications will be 23 | /// imported and generated. 24 | const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512; 25 | 26 | pub type Service = sc_service::PartialComponents< 27 | FullClient, 28 | FullBackend, 29 | FullSelectChain, 30 | sc_consensus::DefaultImportQueue, 31 | sc_transaction_pool::FullPool, 32 | ( 33 | sc_consensus_grandpa::GrandpaBlockImport, 34 | sc_consensus_grandpa::LinkHalf, 35 | Option, 36 | ), 37 | >; 38 | 39 | pub fn new_partial(config: &Configuration) -> Result { 40 | let telemetry = config 41 | .telemetry_endpoints 42 | .clone() 43 | .filter(|x| !x.is_empty()) 44 | .map(|endpoints| -> Result<_, sc_telemetry::Error> { 45 | let worker = TelemetryWorker::new(16)?; 46 | let telemetry = worker.handle().new_telemetry(endpoints); 47 | Ok((worker, telemetry)) 48 | }) 49 | .transpose()?; 50 | 51 | let executor = sc_service::new_wasm_executor::(config); 52 | let (client, backend, keystore_container, task_manager) = 53 | sc_service::new_full_parts::( 54 | config, 55 | telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), 56 | executor, 57 | )?; 58 | let client = Arc::new(client); 59 | 60 | let telemetry = telemetry.map(|(worker, telemetry)| { 61 | task_manager.spawn_handle().spawn("telemetry", None, worker.run()); 62 | telemetry 63 | }); 64 | 65 | let select_chain = sc_consensus::LongestChain::new(backend.clone()); 66 | 67 | let transaction_pool = sc_transaction_pool::BasicPool::new_full( 68 | config.transaction_pool.clone(), 69 | config.role.is_authority().into(), 70 | config.prometheus_registry(), 71 | task_manager.spawn_essential_handle(), 72 | client.clone(), 73 | ); 74 | 75 | let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( 76 | client.clone(), 77 | GRANDPA_JUSTIFICATION_PERIOD, 78 | &client, 79 | select_chain.clone(), 80 | telemetry.as_ref().map(|x| x.handle()), 81 | )?; 82 | 83 | let cidp_client = client.clone(); 84 | let import_queue = 85 | sc_consensus_aura::import_queue::(ImportQueueParams { 86 | block_import: grandpa_block_import.clone(), 87 | justification_import: Some(Box::new(grandpa_block_import.clone())), 88 | client: client.clone(), 89 | create_inherent_data_providers: move |parent_hash, _| { 90 | let cidp_client = cidp_client.clone(); 91 | async move { 92 | let slot_duration = sc_consensus_aura::standalone::slot_duration_at( 93 | &*cidp_client, 94 | parent_hash, 95 | )?; 96 | let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); 97 | 98 | let slot = 99 | sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( 100 | *timestamp, 101 | slot_duration, 102 | ); 103 | 104 | Ok((slot, timestamp)) 105 | } 106 | }, 107 | spawner: &task_manager.spawn_essential_handle(), 108 | registry: config.prometheus_registry(), 109 | check_for_equivocation: Default::default(), 110 | telemetry: telemetry.as_ref().map(|x| x.handle()), 111 | compatibility_mode: Default::default(), 112 | })?; 113 | 114 | Ok(sc_service::PartialComponents { 115 | client, 116 | backend, 117 | task_manager, 118 | import_queue, 119 | keystore_container, 120 | select_chain, 121 | transaction_pool, 122 | other: (grandpa_block_import, grandpa_link, telemetry), 123 | }) 124 | } 125 | 126 | /// Builds a new service for a full client. 127 | pub fn new_full(config: Configuration) -> Result { 128 | let sc_service::PartialComponents { 129 | client, 130 | backend, 131 | mut task_manager, 132 | import_queue, 133 | keystore_container, 134 | select_chain, 135 | transaction_pool, 136 | other: (block_import, grandpa_link, mut telemetry), 137 | } = new_partial(&config)?; 138 | 139 | let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); 140 | 141 | let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( 142 | &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), 143 | &config.chain_spec, 144 | ); 145 | let (grandpa_protocol_config, grandpa_notification_service) = 146 | sc_consensus_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone()); 147 | net_config.add_notification_protocol(grandpa_protocol_config); 148 | 149 | let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( 150 | backend.clone(), 151 | grandpa_link.shared_authority_set().clone(), 152 | Vec::default(), 153 | )); 154 | 155 | let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = 156 | sc_service::build_network(sc_service::BuildNetworkParams { 157 | config: &config, 158 | net_config, 159 | client: client.clone(), 160 | transaction_pool: transaction_pool.clone(), 161 | spawn_handle: task_manager.spawn_handle(), 162 | import_queue, 163 | block_announce_validator_builder: None, 164 | warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), 165 | block_relay: None, 166 | })?; 167 | 168 | if config.offchain_worker.enabled { 169 | task_manager.spawn_handle().spawn( 170 | "offchain-workers-runner", 171 | "offchain-worker", 172 | sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { 173 | runtime_api_provider: client.clone(), 174 | is_validator: config.role.is_authority(), 175 | keystore: Some(keystore_container.keystore()), 176 | offchain_db: backend.offchain_storage(), 177 | transaction_pool: Some(OffchainTransactionPoolFactory::new( 178 | transaction_pool.clone(), 179 | )), 180 | network_provider: network.clone(), 181 | enable_http_requests: true, 182 | custom_extensions: |_| vec![], 183 | }) 184 | .run(client.clone(), task_manager.spawn_handle()) 185 | .boxed(), 186 | ); 187 | } 188 | 189 | let role = config.role.clone(); 190 | let force_authoring = config.force_authoring; 191 | let backoff_authoring_blocks: Option<()> = None; 192 | let name = config.network.node_name.clone(); 193 | let enable_grandpa = !config.disable_grandpa; 194 | let prometheus_registry = config.prometheus_registry().cloned(); 195 | 196 | let rpc_extensions_builder = { 197 | let client = client.clone(); 198 | let pool = transaction_pool.clone(); 199 | 200 | Box::new(move |deny_unsafe, _| { 201 | let deps = 202 | crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; 203 | crate::rpc::create_full(deps).map_err(Into::into) 204 | }) 205 | }; 206 | 207 | let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { 208 | network: network.clone(), 209 | client: client.clone(), 210 | keystore: keystore_container.keystore(), 211 | task_manager: &mut task_manager, 212 | transaction_pool: transaction_pool.clone(), 213 | rpc_builder: rpc_extensions_builder, 214 | backend, 215 | system_rpc_tx, 216 | tx_handler_controller, 217 | sync_service: sync_service.clone(), 218 | config, 219 | telemetry: telemetry.as_mut(), 220 | })?; 221 | 222 | if role.is_authority() { 223 | let proposer_factory = sc_basic_authorship::ProposerFactory::new( 224 | task_manager.spawn_handle(), 225 | client.clone(), 226 | transaction_pool.clone(), 227 | prometheus_registry.as_ref(), 228 | telemetry.as_ref().map(|x| x.handle()), 229 | ); 230 | 231 | let slot_duration = sc_consensus_aura::slot_duration(&*client)?; 232 | 233 | let aura = sc_consensus_aura::start_aura::( 234 | StartAuraParams { 235 | slot_duration, 236 | client, 237 | select_chain, 238 | block_import, 239 | proposer_factory, 240 | create_inherent_data_providers: move |_, ()| async move { 241 | let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); 242 | 243 | let slot = 244 | sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( 245 | *timestamp, 246 | slot_duration, 247 | ); 248 | 249 | Ok((slot, timestamp)) 250 | }, 251 | force_authoring, 252 | backoff_authoring_blocks, 253 | keystore: keystore_container.keystore(), 254 | sync_oracle: sync_service.clone(), 255 | justification_sync_link: sync_service.clone(), 256 | block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), 257 | max_block_proposal_slot_portion: None, 258 | telemetry: telemetry.as_ref().map(|x| x.handle()), 259 | compatibility_mode: Default::default(), 260 | }, 261 | )?; 262 | 263 | // the AURA authoring task is considered essential, i.e. if it 264 | // fails we take down the service with it. 265 | task_manager 266 | .spawn_essential_handle() 267 | .spawn_blocking("aura", Some("block-authoring"), aura); 268 | } 269 | 270 | if enable_grandpa { 271 | // if the node isn't actively participating in consensus then it doesn't 272 | // need a keystore, regardless of which protocol we use below. 273 | let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; 274 | 275 | let grandpa_config = sc_consensus_grandpa::Config { 276 | // FIXME #1578 make this available through chainspec 277 | gossip_duration: Duration::from_millis(333), 278 | justification_generation_period: GRANDPA_JUSTIFICATION_PERIOD, 279 | name: Some(name), 280 | observer_enabled: false, 281 | keystore, 282 | local_role: role, 283 | telemetry: telemetry.as_ref().map(|x| x.handle()), 284 | protocol_name: grandpa_protocol_name, 285 | }; 286 | 287 | // start the full GRANDPA voter 288 | // NOTE: non-authorities could run the GRANDPA observer protocol, but at 289 | // this point the full voter should provide better guarantees of block 290 | // and vote data availability than the observer. The observer has not 291 | // been tested extensively yet and having most nodes in a network run it 292 | // could lead to finality stalls. 293 | let grandpa_config = sc_consensus_grandpa::GrandpaParams { 294 | config: grandpa_config, 295 | link: grandpa_link, 296 | network, 297 | sync: Arc::new(sync_service), 298 | notification_service: grandpa_notification_service, 299 | voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), 300 | prometheus_registry, 301 | shared_voter_state: SharedVoterState::empty(), 302 | telemetry: telemetry.as_ref().map(|x| x.handle()), 303 | offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(transaction_pool), 304 | }; 305 | 306 | // the GRANDPA voter task is considered infallible, i.e. 307 | // if it fails we take down the service with it. 308 | task_manager.spawn_essential_handle().spawn_blocking( 309 | "grandpa-voter", 310 | None, 311 | sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, 312 | ); 313 | } 314 | 315 | network_starter.start_network(); 316 | Ok(task_manager) 317 | } 318 | -------------------------------------------------------------------------------- /pallets/coinflip/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Kaichao'] 3 | description = 'FRAME coin flip game pallet' 4 | edition = '2021' 5 | homepage = 'https://substrate.dev' 6 | license = 'Unlicense' 7 | name = 'pallet-coinflip' 8 | repository = 'https://github.com/kaichaosun/play-substrate/' 9 | version = '4.0.0' 10 | 11 | [package.metadata.docs.rs] 12 | targets = ['x86_64-unknown-linux-gnu'] 13 | 14 | # alias "parity-scale-code" to "codec" 15 | [dependencies.codec] 16 | default-features = false 17 | features = ['derive'] 18 | package = 'parity-scale-codec' 19 | version = '2.0.0' 20 | 21 | [dependencies] 22 | frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-10', version = '4.0.0-dev' } 23 | frame-system = { default-features = false,git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-10', version = '4.0.0-dev' } 24 | sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-10', version = '4.0.0-dev' } 25 | pallet-balances = { default-features = false, git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-10', version = '4.0.0-dev' } 26 | scale-info = { default-features = false, features = ['derive'], version = '1.0' } 27 | 28 | [dev-dependencies] 29 | sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-10', version = '4.0.0-dev' } 30 | sp-io = { default-features = false, git = 'https://github.com/paritytech/substrate.git', tag = 'monthly-2021-10', version = '4.0.0-dev' } 31 | 32 | [features] 33 | default = ['std'] 34 | std = [ 35 | 'codec/std', 36 | 'frame-support/std', 37 | 'frame-system/std', 38 | 'pallet-balances/std', 39 | 'scale-info/std', 40 | 'sp-runtime/std', 41 | ] 42 | -------------------------------------------------------------------------------- /pallets/coinflip/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | /// A FRAME pallet for coin flip game 4 | 5 | pub use pallet::*; 6 | 7 | #[cfg(test)] 8 | mod mock; 9 | 10 | #[cfg(test)] 11 | mod tests; 12 | 13 | #[frame_support::pallet] 14 | pub mod pallet { 15 | use frame_support::{dispatch::DispatchResultWithPostInfo, pallet_prelude::*}; 16 | use frame_system::pallet_prelude::*; 17 | use frame_support::traits::{Currency, WithdrawReasons, ExistenceRequirement, Randomness}; 18 | use sp_runtime::traits::{Zero, Hash, Saturating}; 19 | 20 | // TODO: refactor to inject currency trait 21 | #[pallet::config] 22 | pub trait Config: frame_system::Config + pallet_balances::Config { 23 | type Randomness: Randomness; 24 | 25 | type Event: From> + IsType<::Event>; 26 | } 27 | 28 | #[pallet::pallet] 29 | #[pallet::generate_store(pub(super) trait Store)] 30 | pub struct Pallet(_); 31 | 32 | #[pallet::storage] 33 | #[pallet::getter(fn payment)] 34 | pub type Payment = StorageValue<_, T::Balance>; 35 | 36 | #[pallet::storage] 37 | #[pallet::getter(fn pot)] 38 | pub type Pot = StorageValue<_, T::Balance, ValueQuery>; 39 | 40 | #[pallet::storage] 41 | #[pallet::getter(fn nonce)] 42 | pub type Nonce = StorageValue<_, u64, ValueQuery>; 43 | 44 | #[pallet::event] 45 | #[pallet::generate_deposit(pub(super) fn deposit_event)] 46 | pub enum Event { 47 | PaymentSet(T::Balance), 48 | PlayResult(T::AccountId, T::Balance), 49 | } 50 | 51 | #[pallet::error] 52 | pub enum Error { 53 | NonePaymentValue, 54 | } 55 | 56 | #[pallet::hooks] 57 | impl Hooks> for Pallet {} 58 | 59 | #[pallet::call] 60 | impl Pallet { 61 | /// Start the game by initialize the storage items. 62 | #[pallet::weight(0)] 63 | pub fn set_payment(origin: OriginFor, value: T::Balance) -> DispatchResultWithPostInfo { 64 | // Ensure the function call is a signed message (i.e. a transaction) 65 | ensure_signed(origin)?; 66 | 67 | // If `payment` is not initialized with some value, set the payment 68 | if Self::payment().is_none() { 69 | // set input value to the payment 70 | >::put(value); 71 | 72 | // Initialize jackpot; 73 | >::put(value); 74 | 75 | // Raise an event for the set payment 76 | Self::deposit_event(Event::PaymentSet(value)); 77 | } 78 | 79 | Ok(().into()) 80 | } 81 | 82 | /// This function allow a user to play our coin flip game 83 | #[pallet::weight(0)] 84 | pub fn play(origin: OriginFor) -> DispatchResultWithPostInfo { 85 | // Ensure that the function call is a signed message (i.e. a transaction) 86 | let sender = ensure_signed(origin)?; 87 | 88 | // Ensure the payment storage item has been set 89 | let payment = Self::payment().ok_or(Error::::NonePaymentValue)?; 90 | 91 | // Read our storage values, and place them in memory variables 92 | let mut nonce = Self::nonce(); 93 | let mut pot = Self::pot(); 94 | 95 | // Try to withdraw the payment from the account, making sure that it will not kill the account 96 | let _ = as Currency<_>>::withdraw(&sender, payment, WithdrawReasons::RESERVE.into(), ExistenceRequirement::KeepAlive)?; 97 | 98 | let mut winnings = Zero::zero(); 99 | 100 | // Generate a random seed using randomness_collective_flip pallet 101 | let random_seed = T::Randomness::random_seed().0.using_encoded(T::Hashing::hash); 102 | let seed_arr = random_seed.as_ref(); 103 | 104 | // as_ref returns an array of u8 105 | if seed_arr[seed_arr.len() - 1] < 128 { 106 | // If the user won the coin flip, deposit the pot winnings; cannot fail 107 | let _ = as Currency<_>>::deposit_into_existing(&sender, pot) 108 | .expect("`sender` must exist since a transaction is being make and withdraw will keep alive; qed."); 109 | 110 | // Set the winnings 111 | winnings = pot; 112 | 113 | // Reduce the pot to zero 114 | pot = Zero::zero(); 115 | } 116 | 117 | // No matter the outcome, increase the pot by the payment amount 118 | pot = pot.saturating_add(payment); 119 | 120 | // Increase the nonce 121 | nonce = nonce.wrapping_add(1); 122 | 123 | // Store the updated value for our storage items 124 | >::put(pot); 125 | Nonce::::put(nonce); 126 | 127 | // Raise event for the play result 128 | Self::deposit_event(Event::PlayResult(sender, winnings)); 129 | 130 | Ok(().into()) 131 | } 132 | } 133 | 134 | } -------------------------------------------------------------------------------- /pallets/coinflip/src/mock.rs: -------------------------------------------------------------------------------- 1 | // Creating mock runtime here 2 | 3 | use crate as pallet_coinflip; 4 | use sp_core::H256; 5 | use frame_support::{parameter_types, traits::Randomness}; 6 | use sp_runtime::{ 7 | traits::{BlakeTwo256, IdentityLookup}, testing::Header, 8 | }; 9 | use frame_system as system; 10 | 11 | type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; 12 | type Block = frame_system::mocking::MockBlock; 13 | 14 | // Configure a mock runtime to test the pallet. 15 | frame_support::construct_runtime!( 16 | pub enum Test where 17 | Block = Block, 18 | NodeBlock = Block, 19 | UncheckedExtrinsic = UncheckedExtrinsic, 20 | { 21 | System: frame_system::{Pallet, Call, Config, Storage, Event}, 22 | Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, 23 | CoinFlipModule: pallet_coinflip::{Pallet, Call, Storage, Event}, 24 | } 25 | ); 26 | 27 | parameter_types! { 28 | pub const BlockHashCount: u64 = 250; 29 | pub const SS58Prefix: u8 = 42; 30 | } 31 | 32 | impl system::Config for Test { 33 | type BaseCallFilter = frame_support::traits::Everything; 34 | type BlockWeights = (); 35 | type BlockLength = (); 36 | type DbWeight = (); 37 | type Origin = Origin; 38 | type Call = Call; 39 | type Index = u64; 40 | type BlockNumber = u64; 41 | type Hash = H256; 42 | type Hashing = BlakeTwo256; 43 | type AccountId = u64; 44 | type Lookup = IdentityLookup; 45 | type Header = Header; 46 | type Event = Event; 47 | type BlockHashCount = BlockHashCount; 48 | type Version = (); 49 | type PalletInfo = PalletInfo; 50 | type AccountData = pallet_balances::AccountData; 51 | type OnNewAccount = (); 52 | type OnKilledAccount = (); 53 | type SystemWeightInfo = (); 54 | type SS58Prefix = SS58Prefix; 55 | type OnSetCode = (); 56 | } 57 | 58 | parameter_types! { 59 | pub const ExistentialDeposit: u64 = 1; 60 | pub const MaxReserves: u32 = 2; 61 | } 62 | 63 | impl pallet_balances::Config for Test { 64 | type Balance = u64; 65 | type DustRemoval = (); 66 | type Event = Event; 67 | type ExistentialDeposit = ExistentialDeposit; 68 | type AccountStore = frame_system::Pallet; 69 | type MaxLocks = (); 70 | type MaxReserves = MaxReserves; 71 | type ReserveIdentifier = [u8; 8]; 72 | type WeightInfo = (); 73 | } 74 | 75 | impl Randomness<::Hash, ::BlockNumber> for CoinFlipModule { 76 | fn random(_subject: &[u8]) -> (::Hash, ::BlockNumber) { 77 | let output = match Self::nonce() { 78 | 0 => H256::from_low_u64_be(101), 79 | 1 => H256::from_low_u64_be(150), 80 | _ => panic!("do not use other value except 1,2 for nonce in test cases"), 81 | }; 82 | (output, 0) 83 | } 84 | } 85 | 86 | impl pallet_coinflip::Config for Test { 87 | type Randomness = CoinFlipModule; 88 | type Event = Event; 89 | } 90 | 91 | // pub type System = frame_system::Pallet; 92 | // pub type Balances = pallet_balances::Pallet; 93 | 94 | // This function basically just builds a genesis storage key/value store according to 95 | // our desired mockup. 96 | pub fn new_test_ext() -> sp_io::TestExternalities { 97 | let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); 98 | 99 | pallet_balances::GenesisConfig:: { 100 | balances: vec![ 101 | (1, 10), 102 | (2, 20), 103 | ] 104 | }.assimilate_storage(&mut t).unwrap(); 105 | 106 | t.into() 107 | } 108 | -------------------------------------------------------------------------------- /pallets/coinflip/src/tests.rs: -------------------------------------------------------------------------------- 1 | // Tests to be written here 2 | 3 | use crate::{Error, mock::*}; 4 | use frame_support::{assert_ok, assert_noop}; 5 | use frame_support::traits::Currency; 6 | use sp_runtime::traits::BadOrigin; 7 | use super::*; 8 | use pallet_balances::Error as BalancesError; 9 | 10 | #[test] 11 | fn set_payment_should_work() { 12 | new_test_ext().execute_with(|| { 13 | // asserting the function can be called successfully 14 | assert_ok!(CoinFlipModule::set_payment(Origin::signed(1), 100)); 15 | // asserting that the stored value is equal to what we stored 16 | assert_eq!(CoinFlipModule::payment(), Some(100)); 17 | assert_eq!(CoinFlipModule::pot(), 100); 18 | 19 | // do not update the stored value 20 | assert_ok!(CoinFlipModule::set_payment(Origin::signed(1), 200)); 21 | // asserting that the stored value is equal to what we stored 22 | assert_eq!(CoinFlipModule::payment(), Some(100)); 23 | assert_eq!(CoinFlipModule::pot(), 100); 24 | }); 25 | } 26 | 27 | #[test] 28 | fn play_security_check_should_work() { 29 | new_test_ext().execute_with(|| { 30 | // Test ensure_signed 31 | assert_noop!(CoinFlipModule::play(Origin::root()), BadOrigin); 32 | 33 | // Ensure the correct error if payment not set 34 | assert_noop!(CoinFlipModule::play(Origin::signed(2)), Error::::NonePaymentValue); 35 | 36 | // Check the balances in genesis config 37 | assert_eq!(Balances::total_balance(&2), 20); 38 | 39 | // set payment and pot, higher than the balances 40 | >::put(30); 41 | >::put(30); 42 | 43 | assert_noop!(CoinFlipModule::play(Origin::signed(2)), BalancesError::::InsufficientBalance); 44 | 45 | // set payment and pot, lower than the balances 46 | >::put(10); 47 | >::put(10); 48 | assert_ok!(CoinFlipModule::play(Origin::signed(2))); 49 | }) 50 | } 51 | 52 | #[test] 53 | fn play_should_work_for_win() { 54 | new_test_ext().execute_with(|| { 55 | >::put(10); 56 | >::put(30); 57 | >::put(0); 58 | 59 | assert_ok!(CoinFlipModule::play(Origin::signed(2))); 60 | assert_eq!(CoinFlipModule::payment(), Some(10)); 61 | assert_eq!(CoinFlipModule::pot(), 10); 62 | assert_eq!(Balances::total_balance(&2), 40); // 20 - 10 (payment) + 30 (reward) 63 | assert_eq!(CoinFlipModule::nonce(), 1); 64 | }) 65 | } 66 | 67 | #[test] 68 | fn play_should_work_for_lose() { 69 | new_test_ext().execute_with(|| { 70 | >::put(10); 71 | >::put(30); 72 | >::put(1); 73 | 74 | assert_ok!(CoinFlipModule::play(Origin::signed(2))); 75 | assert_eq!(CoinFlipModule::payment(), Some(10)); 76 | assert_eq!(CoinFlipModule::pot(), 40); 77 | assert_eq!(Balances::total_balance(&2), 10); // 20 - 10 (payment) 78 | assert_eq!(CoinFlipModule::nonce(), 2); 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /pallets/data-type/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Kaichao '] 3 | description = 'A pallet to show different data type of runtime storage.' 4 | edition = '2021' 5 | homepage = 'https://substrate.dev' 6 | license = 'Unlicense' 7 | name = 'pallet-data-type' 8 | repository = 'https://github.com/kaichaosun/play-substrate' 9 | version = '4.0.0-dev' 10 | 11 | [package.metadata.docs.rs] 12 | targets = ['x86_64-unknown-linux-gnu'] 13 | 14 | [dependencies] 15 | codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } 16 | scale-info = { default-features = false, features = ['derive'], version = "2.10.0" } 17 | log = { version = "0.4.14", default-features = false } 18 | frame-support = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 19 | frame-system = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 20 | frame-benchmarking = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0", optional = true } 21 | pallet-timestamp = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 22 | sp-core = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 23 | sp-runtime = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 24 | sp-std = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 25 | 26 | [dev-dependencies] 27 | sp-io = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 28 | 29 | [features] 30 | default = ['std'] 31 | std = [ 32 | 'codec/std', 33 | 'scale-info/std', 34 | 'frame-support/std', 35 | 'frame-system/std', 36 | 'pallet-timestamp/std', 37 | 'sp-core/std', 38 | 'sp-runtime/std', 39 | 'sp-std/std', 40 | ] 41 | runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] 42 | try-runtime = ['frame-support/try-runtime'] 43 | -------------------------------------------------------------------------------- /pallets/data-type/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | /// A module for demo storage data types. 4 | pub use pallet::*; 5 | 6 | #[frame_support::pallet(dev_mode)] 7 | pub mod pallet { 8 | use frame_support::{pallet_prelude::*, Blake2_128Concat, Twox64Concat}; 9 | use frame_system::pallet_prelude::*; 10 | 11 | use scale_info::TypeInfo; 12 | use sp_runtime::{Permill, 13 | traits::{ 14 | Saturating 15 | } 16 | }; 17 | use sp_std::prelude::*; 18 | use sp_core::{H256, U256}; 19 | 20 | #[pallet::config] 21 | pub trait Config: frame_system::Config + pallet_timestamp::Config { 22 | type RuntimeEvent: From> + IsType<::RuntimeEvent>; 23 | } 24 | 25 | #[pallet::pallet] 26 | pub struct Pallet(_); 27 | 28 | // init to be none, store optional value 29 | #[pallet::storage] 30 | #[pallet::getter(fn my_option)] 31 | pub type MyOption = StorageValue<_, u32>; 32 | 33 | #[pallet::type_value] 34 | pub fn DefaultForMyUnsignedNumber() -> u8 { 35 | 10 36 | } 37 | 38 | // store unsigned integer, init to zero if not set, here we set it to 10 39 | #[pallet::storage] 40 | #[pallet::getter(fn unsigned_number)] 41 | pub type MyUnsignedNumber = StorageValue<_, u8, ValueQuery, DefaultForMyUnsignedNumber>; 42 | 43 | // also init to zero, can store negative number 44 | #[pallet::storage] 45 | #[pallet::getter(fn signed_number)] 46 | pub type MySignedNumber = StorageValue<_, i8, ValueQuery>; 47 | 48 | // init to false, store boolean value 49 | #[pallet::storage] 50 | #[pallet::getter(fn my_bool)] 51 | pub type MyBool = StorageValue<_, bool, ValueQuery>; 52 | 53 | // runtime storage should not contain any human readable information 54 | // instead store the encoded limited vector. 55 | // default to 0x00 56 | #[pallet::storage] 57 | #[pallet::getter(fn my_string)] 58 | pub type MyString = StorageValue<_, BoundedVec>, ValueQuery>; 59 | 60 | // float number, Percent, Permill, Perbill 61 | #[pallet::storage] 62 | #[pallet::getter(fn my_permill)] 63 | pub type MyPermill = StorageValue<_, Permill, ValueQuery>; 64 | 65 | // time is usually the type alias of u64 66 | #[pallet::storage] 67 | #[pallet::getter(fn my_time)] 68 | pub type MyTime = StorageValue<_, T::Moment>; 69 | 70 | // AccountId is [u8,32] 71 | #[pallet::storage] 72 | #[pallet::getter(fn my_account_id)] 73 | pub type MyAccountId = StorageValue<_, T::AccountId>; 74 | 75 | // BlockNumber 76 | #[pallet::storage] 77 | #[pallet::getter(fn my_block_number)] 78 | pub type MyBlockNumber = StorageValue<_, BlockNumberFor>; 79 | 80 | // tuple 81 | #[pallet::storage] 82 | #[pallet::getter(fn my_tuple)] 83 | pub type MyTuple = StorageValue<_, (u8, bool)>; 84 | 85 | #[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen)] 86 | pub enum Weekday { 87 | Monday, 88 | Tuesday, 89 | Wednesday, 90 | Other, 91 | } 92 | 93 | // enum 94 | #[pallet::storage] 95 | #[pallet::getter(fn my_enum)] 96 | pub type MyEnum = StorageValue<_, Weekday>; 97 | 98 | #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, Default, TypeInfo, MaxEncodedLen)] 99 | pub struct People { 100 | name: BoundedVec>, 101 | age: u8, 102 | } 103 | 104 | // struct 105 | #[pallet::storage] 106 | #[pallet::getter(fn my_struct)] 107 | pub type MyStruct = StorageValue<_, People>; 108 | 109 | // Fixed Hash 110 | #[pallet::storage] 111 | #[pallet::getter(fn my_fixed_hashuct)] 112 | pub type MyFixedHash = StorageValue<_, H256>; 113 | 114 | // Big integer 115 | #[pallet::storage] 116 | #[pallet::getter(fn my_big_integer)] 117 | pub type MyBigInteger = StorageValue<_, U256>; 118 | 119 | // map 120 | #[pallet::storage] 121 | #[pallet::getter(fn my_map)] 122 | pub type MyMap = StorageMap<_, Twox64Concat, u8, T::Hash>; 123 | 124 | // double map 125 | #[pallet::storage] 126 | #[pallet::getter(fn my_double_map)] 127 | pub type MyDoubleMap = StorageDoubleMap<_, Blake2_128Concat, T::AccountId, Twox64Concat, u32, T::Hash>; 128 | 129 | // storage n map 130 | #[pallet::storage] 131 | #[pallet::getter(fn my_n_map)] 132 | pub type MyNMap = StorageNMap< 133 | _, 134 | ( 135 | NMapKey, 136 | NMapKey>, // owner 137 | NMapKey, // delegate 138 | ), 139 | T::Hash, 140 | >; 141 | 142 | #[pallet::event] 143 | #[pallet::generate_deposit(pub(super) fn deposit_event)] 144 | pub enum Event { 145 | OptionSet(u32), 146 | } 147 | 148 | #[pallet::error] 149 | pub enum Error { 150 | NameTooLong, 151 | StringTooLong, 152 | } 153 | 154 | #[pallet::call] 155 | impl Pallet { 156 | #[pallet::call_index(0)] 157 | #[pallet::weight(0)] 158 | pub fn set_option(origin: OriginFor, value: u32) -> DispatchResult { 159 | ensure_signed(origin)?; 160 | 161 | MyOption::::put(value); 162 | 163 | Self::deposit_event(Event::OptionSet(value)); 164 | Ok(()) 165 | } 166 | 167 | #[pallet::call_index(1)] 168 | #[pallet::weight(0)] 169 | pub fn modify_option(origin: OriginFor) -> DispatchResult { 170 | ensure_signed(origin)?; 171 | 172 | let my_option = MyOption::::get(); 173 | match my_option { 174 | Some(v) => log::info!("old value when modify option: {:?}", v), 175 | None => log::info!("No value store"), 176 | } 177 | 178 | MyOption::::mutate(|my_option| my_option.map(|v| v + 1)); 179 | 180 | Ok(()) 181 | } 182 | 183 | #[pallet::call_index(2)] 184 | #[pallet::weight(0)] 185 | pub fn delete_option(origin: OriginFor) -> DispatchResult { 186 | ensure_signed(origin)?; 187 | 188 | MyOption::::kill(); 189 | 190 | Ok(()) 191 | } 192 | 193 | #[pallet::call_index(3)] 194 | #[pallet::weight(0)] 195 | // this is for demonstration, you should never put all the operations in one call. 196 | pub fn play_number(origin: OriginFor, number: u8) -> DispatchResult { 197 | ensure_signed(origin)?; 198 | 199 | MyUnsignedNumber::::put(number); 200 | 201 | let _my_num = MyUnsignedNumber::::get(); 202 | 203 | MyUnsignedNumber::::mutate(|value| value.saturating_add(1)); 204 | 205 | MyUnsignedNumber::::kill(); 206 | 207 | Ok(()) 208 | } 209 | 210 | #[pallet::call_index(4)] 211 | #[pallet::weight(0)] 212 | // this is for demonstration, you should never put all the operations in one call. 213 | pub fn play_integer(origin: OriginFor, number: i8) -> DispatchResult { 214 | ensure_signed(origin)?; 215 | 216 | MySignedNumber::::put(number); 217 | 218 | let _my_num = MySignedNumber::::get(); 219 | 220 | MySignedNumber::::mutate(|value| value.saturating_sub(1)); 221 | 222 | MySignedNumber::::kill(); 223 | 224 | Ok(()) 225 | } 226 | 227 | #[pallet::call_index(5)] 228 | #[pallet::weight(0)] 229 | // this is for demonstration, you should never put all the operations in one call. 230 | pub fn play_bool(origin: OriginFor, value: bool) -> DispatchResult { 231 | ensure_signed(origin)?; 232 | 233 | MyBool::::put(value); 234 | 235 | let my_bool = MyBool::::get(); 236 | 237 | if my_bool { 238 | log::info!("get true in bool demo"); 239 | } else { 240 | log::info!("get false in bool demo"); 241 | } 242 | 243 | MyBool::::put(!my_bool); 244 | 245 | MyBool::::kill(); 246 | 247 | Ok(()) 248 | } 249 | 250 | #[pallet::call_index(6)] 251 | #[pallet::weight(0)] 252 | // this is for demonstration, you should never put all the operations in one call. 253 | pub fn play_string(origin: OriginFor, value: Vec) -> DispatchResult { 254 | ensure_signed(origin)?; 255 | 256 | let bounded_value = BoundedVec::>::try_from(value.clone()).map_err(|_| Error::::StringTooLong)?; 257 | MyString::::put(bounded_value); 258 | 259 | let my_string = MyString::::get(); 260 | 261 | let _new_string = my_string.iter().map(|v| v + 1); 262 | 263 | if my_string.len() > 10 { 264 | log::info!("string too long in string demo"); 265 | } 266 | 267 | // MyString::kill(); 268 | 269 | Ok(()) 270 | } 271 | 272 | #[pallet::call_index(7)] 273 | #[pallet::weight(0)] 274 | // this is for demonstration, you should never put all the operations in one call. 275 | pub fn play_permill(origin: OriginFor, value: u32) -> DispatchResult { 276 | ensure_signed(origin)?; 277 | 278 | // from_percent needs 1~100 279 | MyPermill::::put(Permill::from_percent(value)); 280 | 281 | let permill_one = Permill::from_parts(1000); 282 | let permill_two = Permill::from_rational(9 as u32,1001 as u32); 283 | let _mul_permil = permill_one.saturating_mul(permill_two); 284 | let _mul_result = permill_two * 20000 as u32; 285 | 286 | let my_permill = MyPermill::::get(); 287 | let calc_result = my_permill * 1_000_000 as u32; 288 | log::info!("get calc result from permill demo: {:?}", calc_result); 289 | 290 | Ok(()) 291 | } 292 | 293 | #[pallet::call_index(8)] 294 | #[pallet::weight(0)] 295 | pub fn play_time(origin: OriginFor) -> DispatchResult { 296 | ensure_signed(origin)?; 297 | 298 | let _now = >::get(); 299 | MyTime::::put(_now); 300 | 301 | Ok(()) 302 | } 303 | 304 | #[pallet::call_index(9)] 305 | #[pallet::weight(0)] 306 | pub fn play_account_id(origin: OriginFor) -> DispatchResult { 307 | let sender = ensure_signed(origin)?; 308 | MyAccountId::::put(sender); 309 | 310 | Ok(()) 311 | } 312 | 313 | #[pallet::call_index(10)] 314 | #[pallet::weight(0)] 315 | pub fn play_block_number(origin: OriginFor) -> DispatchResult { 316 | ensure_signed(origin)?; 317 | // MyBlockNumber::::put(); 318 | 319 | Ok(()) 320 | } 321 | 322 | #[pallet::call_index(11)] 323 | #[pallet::weight(0)] 324 | pub fn play_tuple(origin: OriginFor, first: u8, second: bool) -> DispatchResult { 325 | ensure_signed(origin)?; 326 | 327 | MyTuple::::put((first, second)); 328 | 329 | Ok(()) 330 | } 331 | 332 | #[pallet::call_index(12)] 333 | #[pallet::weight(0)] 334 | pub fn play_enum(origin: OriginFor, weekday: Weekday) -> DispatchResult { 335 | ensure_signed(origin)?; 336 | 337 | MyEnum::::put(weekday); 338 | 339 | Ok(()) 340 | } 341 | 342 | #[pallet::call_index(13)] 343 | #[pallet::weight(0)] 344 | pub fn play_struct(origin: OriginFor, name: Vec, age: u8) -> DispatchResult { 345 | ensure_signed(origin)?; 346 | 347 | let people = People { 348 | name: BoundedVec::>::try_from(name.clone()).map_err(|_| Error::::NameTooLong)?, 349 | age, 350 | }; 351 | MyStruct::::put(people); 352 | 353 | let _my_people = MyStruct::::get(); 354 | 355 | Ok(()) 356 | } 357 | 358 | #[pallet::call_index(14)] 359 | #[pallet::weight(0)] 360 | pub fn insert_map(origin: OriginFor, key: u8, value: T::Hash) -> DispatchResult { 361 | ensure_signed(origin)?; 362 | 363 | MyMap::::insert(key, value); 364 | 365 | MyMap::::contains_key(key); 366 | 367 | let my_value = MyMap::::get(key); 368 | log::info!("get value from map demo: {:?}", my_value); 369 | 370 | MyMap::::remove(key); 371 | 372 | Ok(()) 373 | } 374 | 375 | #[pallet::call_index(15)] 376 | #[pallet::weight(0)] 377 | pub fn insert_double_map(origin: OriginFor, key2: u32, value: T::Hash) -> DispatchResult { 378 | let sender = ensure_signed(origin)?; 379 | 380 | MyDoubleMap::::insert(&sender, key2, value); 381 | 382 | MyDoubleMap::::get(&sender, key2); 383 | 384 | MyDoubleMap::::remove(&sender, key2); 385 | 386 | let _cursor = MyDoubleMap::::clear_prefix(&sender, 100, None); 387 | 388 | Ok(()) 389 | } 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /pallets/genesis-config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Kaichao '] 3 | description = 'A pallet to demo genesis config.' 4 | edition = '2021' 5 | homepage = 'https://substrate.dev' 6 | license = 'Unlicense' 7 | name = 'pallet-genesis-config' 8 | repository = 'https://github.com/kaichaosun/play-substrate/' 9 | version = '4.0.0-dev' 10 | 11 | [package.metadata.docs.rs] 12 | targets = ['x86_64-unknown-linux-gnu'] 13 | 14 | [dependencies] 15 | codec = { default-features = false, features = ['derive'], package = 'parity-scale-codec', version = "2.0.0" } 16 | scale-info = { default-features = false, features = ['derive'], version = '2.10.0' } 17 | frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37", version = '4.0.0-dev' } 18 | frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37", version = '4.0.0-dev' } 19 | sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.37", version = '4.0.0-dev' } 20 | 21 | [features] 22 | default = ['std'] 23 | std = [ 24 | 'codec/std', 25 | 'frame-support/std', 26 | 'frame-system/std', 27 | 'sp-std/std', 28 | 'scale-info/std' 29 | ] 30 | -------------------------------------------------------------------------------- /pallets/genesis-config/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | /// A FRAME pallet for genesis config 4 | 5 | pub use pallet::*; 6 | 7 | #[frame_support::pallet] 8 | pub mod pallet { 9 | use frame_support::{pallet_prelude::*, Blake2_128Concat}; 10 | use frame_system::pallet_prelude::*; 11 | use sp_std::vec::Vec; 12 | 13 | #[pallet::config] 14 | pub trait Config: frame_system::Config { 15 | type Event: From> + IsType<::Event>; 16 | } 17 | 18 | #[pallet::pallet] 19 | #[pallet::generate_store(pub(super) trait Store)] 20 | pub struct Pallet(_); 21 | 22 | // genesis storage with config() 23 | #[pallet::storage] 24 | #[pallet::getter(fn something)] 25 | pub type Something = StorageValue<_, u32>; 26 | 27 | #[pallet::storage] 28 | #[pallet::getter(fn some_map)] 29 | pub type SomeMap = StorageMap<_, Blake2_128Concat, T::AccountId, u32>; 30 | #[pallet::genesis_config] 31 | pub struct GenesisConfig { 32 | pub something: Option, 33 | pub some_map: Vec<(T::AccountId, u32)>, 34 | } 35 | 36 | #[cfg(feature = "std")] 37 | impl Default for GenesisConfig { 38 | fn default() -> Self { 39 | Self { 40 | something: None, 41 | some_map: vec![], 42 | } 43 | } 44 | } 45 | 46 | #[pallet::genesis_build] 47 | impl GenesisBuild for GenesisConfig { 48 | fn build(&self) { 49 | if let Some(ref something) = self.something { 50 | Something::::put(something); 51 | } 52 | for (who, value) in self.some_map.iter() { 53 | SomeMap::::insert(who, value); 54 | } 55 | } 56 | } 57 | 58 | #[pallet::event] 59 | #[pallet::generate_deposit(pub(super) fn deposit_event)] 60 | pub enum Event { 61 | SomethingStored(u32, T::AccountId), 62 | } 63 | 64 | #[pallet::error] 65 | pub enum Error { 66 | } 67 | 68 | #[pallet::hooks] 69 | impl Hooks> for Pallet {} 70 | 71 | #[pallet::call] 72 | impl Pallet { 73 | #[pallet::weight(0)] 74 | pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { 75 | let who = ensure_signed(origin)?; 76 | 77 | Something::::put(something); 78 | 79 | Self::deposit_event(Event::SomethingStored(something, who)); 80 | Ok(()) 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /pallets/ocw-signed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Anonymous'] 3 | description = 'FRAME pallet offchain worker send signed transaction' 4 | edition = '2021' 5 | homepage = 'https://substrate.dev' 6 | license = 'Unlicense' 7 | name = 'pallet-ocw-signed' 8 | repository = 'https://github.com/paritytech/substrate/' 9 | version = '4.0.0' 10 | 11 | [package.metadata.docs.rs] 12 | targets = ['x86_64-unknown-linux-gnu'] 13 | 14 | [dependencies] 15 | alt_serde = { version = "1", default-features = false, features = ["derive"] } 16 | # updated to `alt_serde_json` when latest version supporting feature `alloc` is released 17 | serde_json = { version = "1", default-features = false, git = "https://github.com/Xanewok/json", branch = "no-std", features = ["alloc"] } 18 | 19 | [dependencies.codec] 20 | default-features = false 21 | features = ['derive'] 22 | package = 'parity-scale-codec' 23 | version = '1.3.0' 24 | 25 | [dependencies.serde] 26 | version = "1.0.101" 27 | optional = true 28 | 29 | [dependencies.frame-support] 30 | default-features = false 31 | git = 'https://github.com/paritytech/substrate.git' 32 | tag = 'v2.0.0-rc6' 33 | version = '2.0.0-rc6' 34 | 35 | [dependencies.frame-system] 36 | default-features = false 37 | git = 'https://github.com/paritytech/substrate.git' 38 | tag = 'v2.0.0-rc6' 39 | version = '2.0.0-rc6' 40 | 41 | [dependencies.sp-std] 42 | default-features = false 43 | git = 'https://github.com/paritytech/substrate.git' 44 | tag = 'v2.0.0-rc6' 45 | version = '2.0.0-rc6' 46 | 47 | [dependencies.sp-runtime] 48 | default-features = false 49 | git = 'https://github.com/paritytech/substrate.git' 50 | tag = 'v2.0.0-rc6' 51 | version = '2.0.0-rc6' 52 | 53 | [dependencies.sp-io] 54 | default-features = false 55 | git = 'https://github.com/paritytech/substrate.git' 56 | tag = 'v2.0.0-rc6' 57 | version = '2.0.0-rc6' 58 | 59 | [dependencies.sp-core] 60 | default-features = false 61 | git = 'https://github.com/paritytech/substrate.git' 62 | tag = 'v2.0.0-rc6' 63 | version = '2.0.0-rc6' 64 | 65 | [dependencies.sp-application-crypto] 66 | default-features = false 67 | git = 'https://github.com/paritytech/substrate.git' 68 | tag = 'v2.0.0-rc6' 69 | version = '2.0.0-rc6' 70 | 71 | [features] 72 | default = ['std'] 73 | std = [ 74 | 'serde', 75 | 'codec/std', 76 | 'frame-support/std', 77 | 'frame-system/std', 78 | 'sp-io/std', 79 | 'sp-runtime/std', 80 | 'sp-std/std', 81 | 'sp-core/std', 82 | ] 83 | -------------------------------------------------------------------------------- /pallets/ocw-signed/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | /// A module for offchain worker send unsigned transaction 4 | 5 | use frame_support::{ 6 | debug, 7 | decl_module, 8 | decl_storage, 9 | decl_event, 10 | decl_error, 11 | dispatch::{DispatchResult}, 12 | }; 13 | use frame_system::{ 14 | self as system, 15 | ensure_signed, 16 | offchain::{ 17 | Signer, 18 | CreateSignedTransaction, 19 | SendSignedTransaction, 20 | AppCrypto, 21 | }, 22 | }; 23 | use sp_core::crypto::KeyTypeId; 24 | use sp_std::vec::Vec; 25 | use sp_runtime::{ 26 | offchain::{ 27 | http, 28 | Duration, 29 | }, 30 | }; 31 | use sp_std::prelude::*; 32 | // We use `alt_serde`, and Xanewok-modified `serde_json` so that we can compile the program 33 | // with serde(features `std`) and alt_serde(features `no_std`). 34 | use alt_serde::{Deserialize, Deserializer}; 35 | use codec::{Encode, Decode}; 36 | 37 | #[cfg(test)] 38 | mod mock; 39 | 40 | #[cfg(test)] 41 | mod tests; 42 | 43 | // TODO 44 | // type TokenPrice = u32; 45 | 46 | const MAX_LEN: usize = 64; // TODO configurage 47 | 48 | // Specifying serde path as `alt_serde` 49 | // ref: https://serde.rs/container-attrs.html#crate 50 | #[serde(crate = "alt_serde")] 51 | #[derive(Deserialize, Encode, Decode, Default)] 52 | struct PriceInfo { 53 | #[serde(rename(deserialize = "USD"), deserialize_with = "de_float_to_integer")] 54 | usd: u32, 55 | } 56 | 57 | pub fn de_float_to_integer<'de, D>(de: D) -> Result 58 | where D: Deserializer<'de> { 59 | let f: f32 = Deserialize::deserialize(de)?; 60 | Ok(f as u32) 61 | } 62 | 63 | /// Defines application identifier for crypto keys of this module. 64 | /// 65 | /// Every module that deals with signatures needs to declare its unique identifier for 66 | /// its crypto keys. 67 | /// When offchain worker is signing transactions it's going to request keys of type 68 | /// `KeyTypeId` from the keystore and use the ones it finds to sign the transaction. 69 | /// The keys can be inserted manually via RPC (see `author_insertKey`). 70 | pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"btc!"); 71 | /// Based on the above `KeyTypeId` we need to generate a pallet-specific crypto type wrappers. 72 | /// We can use from supported crypto kinds (`sr25519`, `ed25519` and `ecdsa`) and augment 73 | /// the types with this pallet-specific identifier. 74 | pub mod sr25519 { 75 | use super::KEY_TYPE; 76 | use sp_application_crypto::{app_crypto, sr25519}; 77 | 78 | app_crypto!(sr25519, KEY_TYPE); 79 | 80 | pub type AuthorityId = Public; 81 | } 82 | 83 | /// The pallet's configuration trait. 84 | pub trait Trait: system::Trait + CreateSignedTransaction> { 85 | /// The identifier type for an offchain worker. 86 | type AuthorityId: AppCrypto; 87 | 88 | /// The overarching event type. 89 | type Event: From> + Into<::Event>; 90 | 91 | /// The overarching dispatch call type. 92 | type Call: From>; 93 | 94 | } 95 | 96 | // This pallet's storage items. 97 | decl_storage! { 98 | trait Store for Module as OcwSignedModule { 99 | /// A vector of recently submitted prices. 100 | /// 101 | /// This is used to calculate average price, should have bounded size. 102 | Prices get(fn prices): Vec; 103 | } 104 | } 105 | 106 | // The pallet's events 107 | decl_event!( 108 | pub enum Event where AccountId = ::AccountId { 109 | NewPrice(u32, AccountId), 110 | } 111 | ); 112 | 113 | // The pallet's errors 114 | decl_error! { 115 | pub enum Error for Module { 116 | } 117 | } 118 | 119 | // The pallet's dispatchable functions. 120 | decl_module! { 121 | /// The module declaration. 122 | pub struct Module for enum Call where origin: T::Origin { 123 | // Initializing errors 124 | // this includes information about your errors in the node's metadata. 125 | // it is needed only if you are using errors in your pallet 126 | type Error = Error; 127 | 128 | // Initializing events 129 | // this is needed only if you are using events in your pallet 130 | fn deposit_event() = default; 131 | 132 | #[weight = 0] 133 | pub fn submit_price_signed(origin, price: u32) -> DispatchResult { 134 | let who = ensure_signed(origin)?; 135 | Self::add_price(who, price); 136 | 137 | Ok(()) 138 | } 139 | 140 | fn offchain_worker() { 141 | debug::native::info!("Submit signed: Offchain working starts running"); 142 | 143 | let res = Self::fetch_price_and_send_signed(); 144 | 145 | if let Err(e) = res { 146 | debug::error!("Submit signed: Error happends: {}", e); 147 | } 148 | } 149 | } 150 | } 151 | 152 | impl Module { 153 | fn add_price(who: T::AccountId, price: u32) { 154 | debug::info!("Submit signed: Adding to the prices: {}", price); 155 | Prices::mutate(|prices| { 156 | if prices.len() < MAX_LEN { 157 | prices.push(price); 158 | } else { 159 | prices[price as usize % MAX_LEN] = price; 160 | } 161 | }); 162 | 163 | Self::deposit_event(RawEvent::NewPrice(price, who)); 164 | } 165 | 166 | fn fetch_price_and_send_signed() -> Result<(), &'static str> { 167 | let signer = Signer::::all_accounts(); 168 | if !signer.can_sign() { 169 | return Err( 170 | "No local accounts available. Consider adding one via `author_insertKey` RPC." 171 | )? 172 | } 173 | 174 | // Make an external HTTP request to fetch the current price. 175 | // Note this call will block until response is received. 176 | let price = Self::fetch_price().map_err(|_| "Submit signed: Failed to fetch price")?; 177 | 178 | // Using `send_signed_transaction` associated type we create and submit a transaction 179 | // representing the call, we've just created. 180 | // Submit signed will return a vector of results for all accounts that were found in the 181 | // local keystore with expected `KEY_TYPE`. 182 | let results = signer.send_signed_transaction( 183 | |_account| { 184 | // Received price is wrapped into a call to `submit_price` public function of this pallet. 185 | // This means that the transaction, when executed, will simply call that function passing 186 | // `price` as an argument. 187 | Call::submit_price_signed(price) 188 | } 189 | ); 190 | 191 | for (acc, res) in &results { 192 | match res { 193 | Ok(()) => debug::info!("Submit signed: [{:?}] Submitted price of {} cents", acc.id, price), 194 | Err(e) => debug::error!("Submit signed: [{:?}] Failed to submit transcation, {:?}", acc.id, e), 195 | } 196 | } 197 | 198 | Ok(()) 199 | } 200 | 201 | fn fetch_price() -> Result { 202 | let deadline = sp_io::offchain::timestamp().add(Duration::from_millis(5000)); 203 | // Initiate an external HTTP GET request. 204 | // This is using high-level wrappers from `sp_runtime`, for the low-level calls that 205 | // you can find in `sp_io`. The API is trying to be similar to `reqwest`, but 206 | // since we are running in a custom WASM execution environment we can't simply 207 | // import the library here. 208 | let request = http::Request::get( 209 | "https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD" 210 | ); 211 | // We set the deadline for sending of the request, note that awaiting response can 212 | // have a separate deadline. Next we send the request, before that it's also possible 213 | // to alter request headers or stream body content in case of non-GET requests. 214 | let pending = request 215 | .deadline(deadline) 216 | .send() 217 | .map_err(|_| http::Error::IoError)?; 218 | // The request is already being processed by the host, we are free to do anything 219 | // else in the worker (we can send multiple concurrent requests too). 220 | // At some point however we probably want to check the response though, 221 | // so we can block current thread and wait for it to finish. 222 | // Note that since the request is being driven by the host, we don't have to wait 223 | // for the request to have it complete, we will just not read the response. 224 | let response = pending.try_wait(deadline) 225 | .map_err(|_| http::Error::DeadlineReached)??; 226 | 227 | if response.code != 200 { 228 | debug::warn!("Submit signed: Unexpected status code: {}", response.code); 229 | return Err(http::Error::Unknown); 230 | } 231 | 232 | let body = response.body().collect::>(); 233 | 234 | let body_str = sp_std::str::from_utf8(&body).map_err(|_| { 235 | debug::warn!("Not UTF8 body"); 236 | http::Error::Unknown 237 | })?; 238 | 239 | let price_info: PriceInfo = serde_json::from_str(&body_str).unwrap(); 240 | debug::warn!("Submit Signed: Got price: {} cents", price_info.usd); 241 | 242 | Ok(price_info.usd) 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /pallets/ocw-signed/src/mock.rs: -------------------------------------------------------------------------------- 1 | // // Creating mock runtime here 2 | 3 | // use crate::{Module, Trait}; 4 | // use sp_core::H256; 5 | // use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; 6 | // use sp_runtime::{ 7 | // traits::{BlakeTwo256, IdentityLookup}, testing::Header, Perbill, 8 | // }; 9 | 10 | // impl_outer_origin! { 11 | // pub enum Origin for Test {} 12 | // } 13 | 14 | // // For testing the pallet, we construct most of a mock runtime. This means 15 | // // first constructing a configuration type (`Test`) which `impl`s each of the 16 | // // configuration traits of pallets we want to use. 17 | // #[derive(Clone, Eq, PartialEq)] 18 | // pub struct Test; 19 | // parameter_types! { 20 | // pub const BlockHashCount: u64 = 250; 21 | // pub const MaximumBlockWeight: Weight = 1024; 22 | // pub const MaximumBlockLength: u32 = 2 * 1024; 23 | // pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); 24 | // } 25 | // impl frame_system::Trait for Test { 26 | // type Origin = Origin; 27 | // type Call = (); 28 | // type Index = u64; 29 | // type BlockNumber = u64; 30 | // type Hash = H256; 31 | // type Hashing = BlakeTwo256; 32 | // type AccountId = u64; 33 | // type Lookup = IdentityLookup; 34 | // type Header = Header; 35 | // type Event = (); 36 | // type BlockHashCount = BlockHashCount; 37 | // type MaximumBlockWeight = MaximumBlockWeight; 38 | // type MaximumBlockLength = MaximumBlockLength; 39 | // type AvailableBlockRatio = AvailableBlockRatio; 40 | // type Version = (); 41 | // type ModuleToIndex = (); 42 | // type AccountData = (); 43 | // type OnNewAccount = (); 44 | // type OnKilledAccount = (); 45 | // } 46 | // impl Trait for Test { 47 | // type Event = (); 48 | // } 49 | // pub type TemplateModule = Module; 50 | 51 | // // This function basically just builds a genesis storage key/value store according to 52 | // // our desired mockup. 53 | // pub fn new_test_ext() -> sp_io::TestExternalities { 54 | // frame_system::GenesisConfig::default().build_storage::().unwrap().into() 55 | // } 56 | -------------------------------------------------------------------------------- /pallets/ocw-signed/src/tests.rs: -------------------------------------------------------------------------------- 1 | // // Tests to be written here 2 | 3 | // use crate::{Error, mock::*}; 4 | // use frame_support::{assert_ok, assert_noop}; 5 | 6 | -------------------------------------------------------------------------------- /pallets/ocw-unsigned/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Anonymous'] 3 | description = 'FRAME pallet offchain worker send unsigned transaction' 4 | edition = '2018' 5 | homepage = 'https://substrate.dev' 6 | license = 'Unlicense' 7 | name = 'pallet-ocw-unsigned' 8 | repository = 'https://github.com/paritytech/substrate/' 9 | version = '2.0.0-rc6' 10 | 11 | [package.metadata.docs.rs] 12 | targets = ['x86_64-unknown-linux-gnu'] 13 | 14 | [dependencies] 15 | alt_serde = { version = "1", default-features = false, features = ["derive"] } 16 | # updated to `alt_serde_json` when latest version supporting feature `alloc` is released 17 | serde_json = { version = "1", default-features = false, git = "https://github.com/Xanewok/json", branch = "no-std", features = ["alloc"] } 18 | 19 | [dependencies.codec] 20 | default-features = false 21 | features = ['derive'] 22 | package = 'parity-scale-codec' 23 | version = '1.3.0' 24 | 25 | [dependencies.serde] 26 | version = "1.0.101" 27 | optional = true 28 | 29 | [dependencies.frame-support] 30 | default-features = false 31 | git = 'https://github.com/paritytech/substrate.git' 32 | tag = 'v2.0.0-rc6' 33 | version = '2.0.0-rc6' 34 | 35 | [dependencies.frame-system] 36 | default-features = false 37 | git = 'https://github.com/paritytech/substrate.git' 38 | tag = 'v2.0.0-rc6' 39 | version = '2.0.0-rc6' 40 | 41 | [dependencies.sp-std] 42 | default-features = false 43 | git = 'https://github.com/paritytech/substrate.git' 44 | tag = 'v2.0.0-rc6' 45 | version = '2.0.0-rc6' 46 | 47 | [dependencies.sp-runtime] 48 | default-features = false 49 | git = 'https://github.com/paritytech/substrate.git' 50 | tag = 'v2.0.0-rc6' 51 | version = '2.0.0-rc6' 52 | 53 | [dependencies.sp-io] 54 | default-features = false 55 | git = 'https://github.com/paritytech/substrate.git' 56 | tag = 'v2.0.0-rc6' 57 | version = '2.0.0-rc6' 58 | 59 | [dev-dependencies.sp-core] 60 | default-features = false 61 | git = 'https://github.com/paritytech/substrate.git' 62 | tag = 'v2.0.0-rc6' 63 | version = '2.0.0-rc6' 64 | 65 | [features] 66 | default = ['std'] 67 | std = [ 68 | 'serde', 69 | 'codec/std', 70 | 'frame-support/std', 71 | 'frame-system/std', 72 | 'sp-io/std', 73 | 'sp-runtime/std', 74 | 'sp-std/std', 75 | ] 76 | -------------------------------------------------------------------------------- /pallets/ocw-unsigned/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | /// A module for offchain worker send unsigned transaction 4 | 5 | use frame_support::{ 6 | debug, 7 | decl_module, 8 | decl_storage, 9 | decl_event, 10 | decl_error, 11 | dispatch::{DispatchResult}, 12 | }; 13 | use frame_system::{ 14 | self as system, 15 | ensure_none, 16 | offchain::{ 17 | SubmitTransaction, 18 | SendTransactionTypes, 19 | }, 20 | }; 21 | use sp_std::vec::Vec; 22 | use sp_runtime::{ 23 | offchain::{ 24 | http, 25 | Duration, 26 | }, 27 | transaction_validity::{ 28 | InvalidTransaction, 29 | ValidTransaction, 30 | TransactionValidity, 31 | TransactionSource, 32 | }, 33 | }; 34 | use codec::{Encode, Decode}; 35 | use sp_std::prelude::*; 36 | // We use `alt_serde`, and Xanewok-modified `serde_json` so that we can compile the program 37 | // with serde(features `std`) and alt_serde(features `no_std`). 38 | use alt_serde::{Deserialize, Deserializer}; 39 | 40 | #[cfg(test)] 41 | mod mock; 42 | 43 | #[cfg(test)] 44 | mod tests; 45 | 46 | // TODO 47 | // type TokenPrice = u32; 48 | 49 | const MAX_LEN: usize = 64; // TODO configurage 50 | 51 | // Specifying serde path as `alt_serde` 52 | // ref: https://serde.rs/container-attrs.html#crate 53 | #[serde(crate = "alt_serde")] 54 | #[derive(Deserialize, Encode, Decode, Default)] 55 | struct PriceInfo { 56 | #[serde(rename(deserialize = "USD"), deserialize_with = "de_float_to_integer")] 57 | usd: u32, 58 | } 59 | 60 | pub fn de_float_to_integer<'de, D>(de: D) -> Result 61 | where D: Deserializer<'de> { 62 | let f: f32 = Deserialize::deserialize(de)?; 63 | Ok(f as u32) 64 | } 65 | 66 | /// The pallet's configuration trait. 67 | pub trait Trait: system::Trait + SendTransactionTypes> { 68 | /// The overarching event type. 69 | type Event: From> + Into<::Event>; 70 | 71 | /// The overarching dispatch call type. 72 | type Call: From>; 73 | 74 | } 75 | 76 | // This pallet's storage items. 77 | decl_storage! { 78 | trait Store for Module as OcwUnsignedModule { 79 | /// A vector of recently submitted prices. 80 | /// 81 | /// This is used to calculate average price, should have bounded size. 82 | Prices get(fn prices): Vec; 83 | } 84 | } 85 | 86 | // The pallet's events 87 | decl_event!( 88 | pub enum Event where AccountId = ::AccountId { 89 | NewPrice(u32, AccountId), 90 | } 91 | ); 92 | 93 | // The pallet's errors 94 | decl_error! { 95 | pub enum Error for Module { 96 | ParseError, 97 | } 98 | } 99 | 100 | // The pallet's dispatchable functions. 101 | decl_module! { 102 | /// The module declaration. 103 | pub struct Module for enum Call where origin: T::Origin { 104 | // Initializing errors 105 | // this includes information about your errors in the node's metadata. 106 | // it is needed only if you are using errors in your pallet 107 | type Error = Error; 108 | 109 | // Initializing events 110 | // this is needed only if you are using events in your pallet 111 | fn deposit_event() = default; 112 | 113 | #[weight = 0] 114 | pub fn submit_price_unsigned(origin, price: u32) -> DispatchResult { 115 | // This ensures that the function can only be called via unsigned transaction. 116 | ensure_none(origin)?; 117 | 118 | // Add the price to the onchain storage, but mark it as coming from an empty address. 119 | Self::add_price(Default::default(), price); 120 | 121 | Ok(()) 122 | } 123 | 124 | fn offchain_worker() { 125 | debug::native::info!("Offchain working starts running"); 126 | 127 | let res = Self::fetch_price_and_send_unsigned(); 128 | 129 | if let Err(e) = res { 130 | debug::error!("Error happends: {}", e); 131 | } 132 | } 133 | } 134 | } 135 | 136 | impl Module { 137 | fn add_price(who: T::AccountId, price: u32) { 138 | debug::info!("Adding to the prices: {}", price); 139 | Prices::mutate(|prices| { 140 | if prices.len() < MAX_LEN { 141 | prices.push(price); 142 | } else { 143 | prices[price as usize % MAX_LEN] = price; 144 | } 145 | }); 146 | 147 | Self::deposit_event(RawEvent::NewPrice(price, who)); 148 | } 149 | 150 | fn fetch_price_and_send_unsigned() -> Result<(), &'static str> { 151 | 152 | let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?; 153 | 154 | let call = Call::submit_price_unsigned(price); 155 | 156 | SubmitTransaction::>::submit_unsigned_transaction(call.into()) 157 | .map_err(|()| "Unable to submit unsigned transaction")?; 158 | 159 | Ok(()) 160 | } 161 | 162 | fn fetch_price() -> Result { 163 | let deadline = sp_io::offchain::timestamp().add(Duration::from_millis(2000)); 164 | // Initiate an external HTTP GET request. 165 | // This is using high-level wrappers from `sp_runtime`, for the low-level calls that 166 | // you can find in `sp_io`. The API is trying to be similar to `reqwest`, but 167 | // since we are running in a custom WASM execution environment we can't simply 168 | // import the library here. 169 | let request = http::Request::get( 170 | "https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD" 171 | ); 172 | // We set the deadline for sending of the request, note that awaiting response can 173 | // have a separate deadline. Next we send the request, before that it's also possible 174 | // to alter request headers or stream body content in case of non-GET requests. 175 | let pending = request 176 | .deadline(deadline) 177 | .send() 178 | .map_err(|_| http::Error::IoError)?; 179 | // The request is already being processed by the host, we are free to do anything 180 | // else in the worker (we can send multiple concurrent requests too). 181 | // At some point however we probably want to check the response though, 182 | // so we can block current thread and wait for it to finish. 183 | // Note that since the request is being driven by the host, we don't have to wait 184 | // for the request to have it complete, we will just not read the response. 185 | let response = pending.try_wait(deadline) 186 | .map_err(|_| http::Error::DeadlineReached)??; 187 | 188 | if response.code != 200 { 189 | debug::warn!("Unexpected status code: {}", response.code); 190 | return Err(http::Error::Unknown); 191 | } 192 | 193 | let body = response.body().collect::>(); 194 | 195 | let body_str = sp_std::str::from_utf8(&body).map_err(|_| { 196 | debug::warn!("Not UTF8 body"); 197 | http::Error::Unknown 198 | })?; 199 | 200 | let price_info: PriceInfo = serde_json::from_str(&body_str).unwrap(); 201 | 202 | debug::warn!("Got price: {} cents", price_info.usd); 203 | 204 | Ok(price_info.usd) 205 | } 206 | 207 | } 208 | 209 | impl frame_support::unsigned::ValidateUnsigned for Module { 210 | type Call = Call; 211 | 212 | fn validate_unsigned( 213 | _source: TransactionSource, 214 | call: &Self::Call 215 | ) -> TransactionValidity { 216 | match call { 217 | Call::submit_price_unsigned(input) => 218 | ValidTransaction::with_tag_prefix("OffchainWorkerUnsignedTx") 219 | .and_provides(input) 220 | .build(), 221 | _ => InvalidTransaction::Call.into() 222 | } 223 | } 224 | } -------------------------------------------------------------------------------- /pallets/ocw-unsigned/src/mock.rs: -------------------------------------------------------------------------------- 1 | // Creating mock runtime here 2 | 3 | use crate::{Module, Trait}; 4 | use sp_core::H256; 5 | use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; 6 | use sp_runtime::{ 7 | traits::{BlakeTwo256, IdentityLookup}, testing::Header, Perbill, 8 | }; 9 | 10 | impl_outer_origin! { 11 | pub enum Origin for Test {} 12 | } 13 | 14 | // For testing the pallet, we construct most of a mock runtime. This means 15 | // first constructing a configuration type (`Test`) which `impl`s each of the 16 | // configuration traits of pallets we want to use. 17 | #[derive(Clone, Eq, PartialEq)] 18 | pub struct Test; 19 | parameter_types! { 20 | pub const BlockHashCount: u64 = 250; 21 | pub const MaximumBlockWeight: Weight = 1024; 22 | pub const MaximumBlockLength: u32 = 2 * 1024; 23 | pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); 24 | } 25 | impl frame_system::Trait for Test { 26 | type Origin = Origin; 27 | type Call = (); 28 | type Index = u64; 29 | type BlockNumber = u64; 30 | type Hash = H256; 31 | type Hashing = BlakeTwo256; 32 | type AccountId = u64; 33 | type Lookup = IdentityLookup; 34 | type Header = Header; 35 | type Event = (); 36 | type BlockHashCount = BlockHashCount; 37 | type MaximumBlockWeight = MaximumBlockWeight; 38 | type MaximumBlockLength = MaximumBlockLength; 39 | type AvailableBlockRatio = AvailableBlockRatio; 40 | type Version = (); 41 | type ModuleToIndex = (); 42 | type AccountData = (); 43 | type OnNewAccount = (); 44 | type OnKilledAccount = (); 45 | } 46 | impl Trait for Test { 47 | type Event = (); 48 | } 49 | pub type TemplateModule = Module; 50 | 51 | // This function basically just builds a genesis storage key/value store according to 52 | // our desired mockup. 53 | pub fn new_test_ext() -> sp_io::TestExternalities { 54 | frame_system::GenesisConfig::default().build_storage::().unwrap().into() 55 | } 56 | -------------------------------------------------------------------------------- /pallets/ocw-unsigned/src/tests.rs: -------------------------------------------------------------------------------- 1 | // Tests to be written here 2 | 3 | use crate::{Error, mock::*}; 4 | use frame_support::{assert_ok, assert_noop}; 5 | 6 | -------------------------------------------------------------------------------- /pallets/poe/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = 'pallet-poe' 3 | version = '4.0.0-dev' 4 | description = 'FRAME proof of existence pallet' 5 | authors = ['Kaichao Sun '] 6 | homepage = 'https://whisperd.tech' 7 | edition = '2021' 8 | license = 'Unlicense' 9 | repository = 'https://github.com/kaichaosun/play-substrate/' 10 | 11 | [package.metadata.docs.rs] 12 | targets = ['x86_64-unknown-linux-gnu'] 13 | 14 | [dependencies] 15 | codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } 16 | scale-info = { default-features = false, features = ['derive'], version = "2.10.0" } 17 | 18 | sp-std = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 19 | 20 | frame-support = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 21 | frame-system = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 22 | frame-benchmarking = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0", optional = true } 23 | 24 | [dev-dependencies] 25 | sp-core = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 26 | sp-io = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 27 | sp-runtime = { default-features = false, git = 'https://github.com/paritytech/polkadot-sdk.git', tag = "polkadot-v1.8.0" } 28 | 29 | [features] 30 | default = ['std'] 31 | std = [ 32 | 'codec/std', 33 | 'frame-support/std', 34 | 'frame-system/std', 35 | 'scale-info/std', 36 | 'sp-std/std', 37 | ] 38 | runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] 39 | try-runtime = ['frame-support/try-runtime'] 40 | -------------------------------------------------------------------------------- /pallets/poe/README.md: -------------------------------------------------------------------------------- 1 | # Proof of Existence 2 | 3 | ## Run benchmark 4 | 5 | ```shell 6 | # compile with runtime-benchmarks feature 7 | cargo build --release --features runtime-benchmarks 8 | 9 | # benchmark dispatchables in poe pallet 10 | # download frame-weight-template.hbs from [Substrate repo](https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs). 11 | ./target/release/node-template benchmark pallet --chain dev --execution wasm --wasm-execution compiled --pallet pallet_poe --extrinsic "*" --steps 20 --repeat 10 --output ./pallets/poe/src/weights.rs --template .maintain/frame-weight-template.hbs 12 | ``` 13 | -------------------------------------------------------------------------------- /pallets/poe/src/benchmarking.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use frame_benchmarking::v1::{benchmarks, whitelisted_caller, account}; 3 | use frame_system::RawOrigin; 4 | use sp_std::vec; 5 | 6 | fn assert_last_event(generic_event: ::RuntimeEvent) { 7 | frame_system::Pallet::::assert_last_event(generic_event.into()); 8 | } 9 | 10 | benchmarks! { 11 | create_claim { 12 | let d in 0 .. T::MaxClaimLength::get(); 13 | let claim = BoundedVec::try_from(vec![0; d as usize]).unwrap(); 14 | let caller: T::AccountId = whitelisted_caller(); 15 | }: _(RawOrigin::Signed(caller.clone()), claim.clone()) 16 | verify { 17 | assert_last_event::(Event::ClaimCreated(caller, claim).into()) 18 | } 19 | 20 | revoke_claim { 21 | let d in 0 .. T::MaxClaimLength::get(); 22 | let claim = BoundedVec::try_from(vec![0; d as usize]).unwrap(); 23 | let caller: T::AccountId = whitelisted_caller(); 24 | assert!(Pallet::::create_claim(RawOrigin::Signed(caller.clone()).into(), claim.clone()).is_ok()); 25 | }: _(RawOrigin::Signed(caller.clone()), claim.clone()) 26 | verify { 27 | assert_last_event::(Event::ClaimRevoked(caller, claim).into()) 28 | } 29 | 30 | transfer_claim { 31 | let d in 0 .. T::MaxClaimLength::get(); 32 | let claim = BoundedVec::try_from(vec![0; d as usize]).unwrap(); 33 | let caller: T::AccountId = whitelisted_caller(); 34 | let target: T::AccountId = account("target", 0, 0); 35 | assert!(Pallet::::create_claim(RawOrigin::Signed(caller.clone()).into(), claim.clone()).is_ok()); 36 | }: _(RawOrigin::Signed(caller), claim, target) 37 | 38 | impl_benchmark_test_suite!(PoeModule, crate::mock::new_test_ext(), crate::mock::Test); 39 | } 40 | -------------------------------------------------------------------------------- /pallets/poe/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | /// A module for proof of existence 4 | 5 | use frame_support::pallet_prelude::*; 6 | use frame_system::pallet_prelude::*; 7 | pub use pallet::*; 8 | pub use weights::WeightInfo; 9 | 10 | #[cfg(test)] 11 | mod mock; 12 | #[cfg(test)] 13 | mod tests; 14 | #[cfg(feature = "runtime-benchmarks")] 15 | mod benchmarking; 16 | 17 | pub mod weights; 18 | 19 | #[frame_support::pallet] 20 | pub mod pallet { 21 | use super::*; 22 | 23 | #[pallet::config] 24 | pub trait Config: frame_system::Config { 25 | /// The maximum length of claim that can be added. 26 | #[pallet::constant] 27 | type MaxClaimLength: Get; 28 | /// The runtime event 29 | type RuntimeEvent: From> + IsType<::RuntimeEvent>; 30 | /// Weight information for extrinsics in this pallet 31 | type WeightInfo: WeightInfo; 32 | } 33 | 34 | #[pallet::pallet] 35 | pub struct Pallet(_); 36 | 37 | #[pallet::storage] 38 | #[pallet::getter(fn proofs)] 39 | pub type Proofs = StorageMap< 40 | _, 41 | Blake2_128Concat, 42 | BoundedVec, 43 | (T::AccountId, BlockNumberFor), 44 | >; 45 | 46 | #[pallet::event] 47 | #[pallet::generate_deposit(pub(super) fn deposit_event)] 48 | pub enum Event { 49 | ClaimCreated(T::AccountId, BoundedVec), 50 | ClaimRevoked(T::AccountId, BoundedVec), 51 | } 52 | 53 | #[pallet::error] 54 | pub enum Error { 55 | ProofAlreadyExist, 56 | ClaimTooLong, 57 | ClaimNotExist, 58 | NotClaimOwner, 59 | } 60 | 61 | #[pallet::hooks] 62 | impl Hooks> for Pallet {} 63 | 64 | #[pallet::call] 65 | impl Pallet { 66 | #[pallet::call_index(0)] 67 | #[pallet::weight(T::WeightInfo::create_claim(claim.len() as u32))] 68 | pub fn create_claim(origin: OriginFor, claim: BoundedVec) -> DispatchResultWithPostInfo { 69 | let sender = ensure_signed(origin)?; 70 | 71 | ensure!(!Proofs::::contains_key(&claim), Error::::ProofAlreadyExist); 72 | 73 | Proofs::::insert( 74 | &claim, 75 | (sender.clone(), frame_system::Pallet::::block_number()), 76 | ); 77 | 78 | Self::deposit_event(Event::ClaimCreated(sender, claim)); 79 | 80 | Ok(().into()) 81 | } 82 | 83 | #[pallet::call_index(1)] 84 | #[pallet::weight(T::WeightInfo::revoke_claim(claim.len() as u32))] 85 | pub fn revoke_claim(origin: OriginFor, claim: BoundedVec) -> DispatchResultWithPostInfo { 86 | let sender = ensure_signed(origin)?; 87 | 88 | let (owner, _) = Proofs::::get(&claim).ok_or(Error::::ClaimNotExist)?; 89 | ensure!(owner == sender, Error::::NotClaimOwner); 90 | 91 | Proofs::::remove(&claim); 92 | 93 | Self::deposit_event(Event::ClaimRevoked(sender, claim)); 94 | 95 | Ok(().into()) 96 | } 97 | 98 | #[pallet::call_index(2)] 99 | #[pallet::weight(T::WeightInfo::transfer_claim(claim.len() as u32))] 100 | pub fn transfer_claim( 101 | origin: OriginFor, 102 | claim: BoundedVec, 103 | dest: T::AccountId, 104 | ) -> DispatchResultWithPostInfo { 105 | let sender = ensure_signed(origin)?; 106 | 107 | let (owner, _block_number) = 108 | Proofs::::get(&claim).ok_or(Error::::ClaimNotExist)?; 109 | ensure!(owner == sender, Error::::NotClaimOwner); 110 | 111 | Proofs::::insert(&claim, (dest, frame_system::Pallet::::block_number())); 112 | 113 | Ok(().into()) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /pallets/poe/src/mock.rs: -------------------------------------------------------------------------------- 1 | use crate as pallet_poe; 2 | use frame_support::traits::{ConstU16, ConstU32, ConstU64}; 3 | use sp_core::H256; 4 | use sp_runtime::{ 5 | traits::{BlakeTwo256, IdentityLookup}, 6 | BuildStorage, 7 | }; 8 | 9 | type Block = frame_system::mocking::MockBlock; 10 | 11 | // Configure a mock runtime to test the pallet. 12 | frame_support::construct_runtime!( 13 | pub enum Test 14 | { 15 | System: frame_system, 16 | PoeModule: pallet_poe, 17 | } 18 | ); 19 | 20 | impl frame_system::Config for Test { 21 | type BaseCallFilter = frame_support::traits::Everything; 22 | type BlockWeights = (); 23 | type BlockLength = (); 24 | type DbWeight = (); 25 | type RuntimeOrigin = RuntimeOrigin; 26 | type RuntimeCall = RuntimeCall; 27 | type Nonce = u64; 28 | type Hash = H256; 29 | type Hashing = BlakeTwo256; 30 | type AccountId = u64; 31 | type Lookup = IdentityLookup; 32 | type Block = Block; 33 | type RuntimeEvent = RuntimeEvent; 34 | type BlockHashCount = ConstU64<250>; 35 | type Version = (); 36 | type PalletInfo = PalletInfo; 37 | type AccountData = (); 38 | type OnNewAccount = (); 39 | type OnKilledAccount = (); 40 | type SystemWeightInfo = (); 41 | type SS58Prefix = ConstU16<42>; 42 | type OnSetCode = (); 43 | type MaxConsumers = frame_support::traits::ConstU32<16>; 44 | } 45 | 46 | impl pallet_poe::Config for Test { 47 | type RuntimeEvent = RuntimeEvent; 48 | type MaxClaimLength = ConstU32<10>; 49 | type WeightInfo = (); 50 | } 51 | 52 | // BUild genesis storage according to the mock runtime. 53 | pub fn new_test_ext() -> sp_io::TestExternalities { 54 | frame_system::GenesisConfig::::default().build_storage().unwrap().into() 55 | } 56 | -------------------------------------------------------------------------------- /pallets/poe/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::{mock::*, Error}; 3 | use frame_support::{assert_noop, assert_ok, BoundedVec}; 4 | 5 | #[test] 6 | fn create_claim_works() { 7 | new_test_ext().execute_with(|| { 8 | let claim = BoundedVec::try_from(vec![0, 1]).unwrap(); 9 | assert_ok!(PoeModule::create_claim(RuntimeOrigin::signed(1), claim.clone())); 10 | 11 | assert_eq!( 12 | Proofs::::get(&claim), 13 | Some((1, frame_system::Pallet::::block_number())) 14 | ); 15 | assert_eq!(<::MaxClaimLength as Get>::get(), 10); 16 | }) 17 | } 18 | 19 | #[test] 20 | fn create_claim_failed_when_claim_already_exist() { 21 | new_test_ext().execute_with(|| { 22 | let claim = BoundedVec::try_from(vec![0, 1]).unwrap(); 23 | let _ = PoeModule::create_claim(RuntimeOrigin::signed(1), claim.clone()); 24 | 25 | assert_noop!( 26 | PoeModule::create_claim(RuntimeOrigin::signed(1), claim.clone()), 27 | Error::::ProofAlreadyExist 28 | ); 29 | }) 30 | } 31 | 32 | #[test] 33 | fn revoke_claim_works() { 34 | new_test_ext().execute_with(|| { 35 | let claim = BoundedVec::try_from(vec![0, 1]).unwrap(); 36 | let _ = PoeModule::create_claim(RuntimeOrigin::signed(1), claim.clone()); 37 | 38 | assert_ok!(PoeModule::revoke_claim(RuntimeOrigin::signed(1), claim.clone())); 39 | }) 40 | } 41 | 42 | #[test] 43 | fn revoke_claim_failed_when_claim_is_not_exist() { 44 | new_test_ext().execute_with(|| { 45 | let claim = BoundedVec::try_from(vec![0, 1]).unwrap(); 46 | 47 | assert_noop!( 48 | PoeModule::revoke_claim(RuntimeOrigin::signed(1), claim.clone()), 49 | Error::::ClaimNotExist 50 | ); 51 | }) 52 | } 53 | 54 | #[test] 55 | fn revoke_claim_failed_with_wrong_owner() { 56 | new_test_ext().execute_with(|| { 57 | let claim = BoundedVec::try_from(vec![0, 1]).unwrap(); 58 | let _ = PoeModule::create_claim(RuntimeOrigin::signed(1), claim.clone()); 59 | 60 | assert_noop!( 61 | PoeModule::revoke_claim(RuntimeOrigin::signed(2), claim.clone()), 62 | Error::::NotClaimOwner 63 | ); 64 | }) 65 | } 66 | 67 | #[test] 68 | fn transfer_claim_works() { 69 | new_test_ext().execute_with(|| { 70 | let claim = BoundedVec::try_from(vec![0, 1]).unwrap(); 71 | let _ = PoeModule::create_claim(RuntimeOrigin::signed(1), claim.clone()); 72 | 73 | assert_ok!(PoeModule::transfer_claim(RuntimeOrigin::signed(1), claim.clone(), 2)); 74 | 75 | let bounded_claim = 76 | BoundedVec::::MaxClaimLength>::try_from(claim.clone()).unwrap(); 77 | assert_eq!( 78 | Proofs::::get(&bounded_claim), 79 | Some((2, frame_system::Pallet::::block_number())) 80 | ); 81 | }) 82 | } 83 | 84 | #[test] 85 | fn transfer_claim_failed_when_claim_is_not_exist() { 86 | new_test_ext().execute_with(|| { 87 | let claim = BoundedVec::try_from(vec![0, 1]).unwrap(); 88 | 89 | assert_noop!( 90 | PoeModule::transfer_claim(RuntimeOrigin::signed(1), claim.clone(), 2), 91 | Error::::ClaimNotExist 92 | ); 93 | }) 94 | } 95 | 96 | #[test] 97 | fn transfer_claim_failed_with_wrong_owner() { 98 | new_test_ext().execute_with(|| { 99 | let claim = BoundedVec::try_from(vec![0, 1]).unwrap(); 100 | let _ = PoeModule::create_claim(RuntimeOrigin::signed(1), claim.clone()); 101 | 102 | assert_noop!( 103 | PoeModule::transfer_claim(RuntimeOrigin::signed(2), claim.clone(), 3), 104 | Error::::NotClaimOwner 105 | ); 106 | }) 107 | } 108 | -------------------------------------------------------------------------------- /pallets/poe/src/weights.rs: -------------------------------------------------------------------------------- 1 | 2 | //! Autogenerated weights for pallet_poe 3 | //! 4 | //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev 5 | //! DATE: 2023-10-23, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` 6 | //! WORST CASE MAP SIZE: `1000000` 7 | //! HOSTNAME: `Kaichaos-MacBook-Pro-720.local`, CPU: `` 8 | //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 9 | 10 | // Executed Command: 11 | // ./target/release/node-template 12 | // benchmark 13 | // pallet 14 | // --chain 15 | // dev 16 | // --execution 17 | // wasm 18 | // --wasm-execution 19 | // compiled 20 | // --pallet 21 | // pallet_poe 22 | // --extrinsic 23 | // * 24 | // --steps 25 | // 20 26 | // --repeat 27 | // 10 28 | // --output 29 | // ./pallets/poe/src/weights.rs 30 | // --template 31 | // .maintain/frame-weight-template.hbs 32 | 33 | #![cfg_attr(rustfmt, rustfmt_skip)] 34 | #![allow(unused_parens)] 35 | #![allow(unused_imports)] 36 | 37 | use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; 38 | use sp_std::marker::PhantomData; 39 | 40 | /// Weight functions needed for pallet_poe. 41 | pub trait WeightInfo { 42 | fn create_claim(d: u32, ) -> Weight; 43 | fn revoke_claim(d: u32, ) -> Weight; 44 | fn transfer_claim(d: u32, ) -> Weight; 45 | } 46 | 47 | /// Weights for pallet_poe using the Substrate node and recommended hardware. 48 | pub struct SubstrateWeight(PhantomData); 49 | impl WeightInfo for SubstrateWeight { 50 | /// Storage: `PoeModule::Proofs` (r:1 w:1) 51 | /// Proof: `PoeModule::Proofs` (`max_values`: None, `max_size`: Some(63), added: 2538, mode: `MaxEncodedLen`) 52 | /// The range of component `d` is `[0, 10]`. 53 | fn create_claim(d: u32, ) -> Weight { 54 | // Proof Size summary in bytes: 55 | // Measured: `6` 56 | // Estimated: `3528` 57 | // Minimum execution time: 9_000_000 picoseconds. 58 | Weight::from_parts(10_000_955, 3528) 59 | // Standard Error: 4_590 60 | .saturating_add(Weight::from_parts(4_013, 0).saturating_mul(d.into())) 61 | .saturating_add(T::DbWeight::get().reads(1_u64)) 62 | .saturating_add(T::DbWeight::get().writes(1_u64)) 63 | } 64 | /// Storage: `PoeModule::Proofs` (r:1 w:1) 65 | /// Proof: `PoeModule::Proofs` (`max_values`: None, `max_size`: Some(63), added: 2538, mode: `MaxEncodedLen`) 66 | /// The range of component `d` is `[0, 10]`. 67 | fn revoke_claim(d: u32, ) -> Weight { 68 | // Proof Size summary in bytes: 69 | // Measured: `85 + d * (1 ±0)` 70 | // Estimated: `3528` 71 | // Minimum execution time: 10_000_000 picoseconds. 72 | Weight::from_parts(10_548_865, 3528) 73 | // Standard Error: 15_304 74 | .saturating_add(Weight::from_parts(27_459, 0).saturating_mul(d.into())) 75 | .saturating_add(T::DbWeight::get().reads(1_u64)) 76 | .saturating_add(T::DbWeight::get().writes(1_u64)) 77 | } 78 | /// Storage: `PoeModule::Proofs` (r:1 w:1) 79 | /// Proof: `PoeModule::Proofs` (`max_values`: None, `max_size`: Some(63), added: 2538, mode: `MaxEncodedLen`) 80 | /// The range of component `d` is `[0, 10]`. 81 | fn transfer_claim(d: u32, ) -> Weight { 82 | // Proof Size summary in bytes: 83 | // Measured: `85 + d * (1 ±0)` 84 | // Estimated: `3528` 85 | // Minimum execution time: 7_000_000 picoseconds. 86 | Weight::from_parts(7_716_488, 3528) 87 | // Standard Error: 10_996 88 | .saturating_add(Weight::from_parts(31_472, 0).saturating_mul(d.into())) 89 | .saturating_add(T::DbWeight::get().reads(1_u64)) 90 | .saturating_add(T::DbWeight::get().writes(1_u64)) 91 | } 92 | } 93 | 94 | // For backwards compatibility and tests 95 | impl WeightInfo for () { 96 | /// Storage: `PoeModule::Proofs` (r:1 w:1) 97 | /// Proof: `PoeModule::Proofs` (`max_values`: None, `max_size`: Some(63), added: 2538, mode: `MaxEncodedLen`) 98 | /// The range of component `d` is `[0, 10]`. 99 | fn create_claim(d: u32, ) -> Weight { 100 | // Proof Size summary in bytes: 101 | // Measured: `6` 102 | // Estimated: `3528` 103 | // Minimum execution time: 9_000_000 picoseconds. 104 | Weight::from_parts(10_000_955, 3528) 105 | // Standard Error: 4_590 106 | .saturating_add(Weight::from_parts(4_013, 0).saturating_mul(d.into())) 107 | .saturating_add(RocksDbWeight::get().reads(1_u64)) 108 | .saturating_add(RocksDbWeight::get().writes(1_u64)) 109 | } 110 | /// Storage: `PoeModule::Proofs` (r:1 w:1) 111 | /// Proof: `PoeModule::Proofs` (`max_values`: None, `max_size`: Some(63), added: 2538, mode: `MaxEncodedLen`) 112 | /// The range of component `d` is `[0, 10]`. 113 | fn revoke_claim(d: u32, ) -> Weight { 114 | // Proof Size summary in bytes: 115 | // Measured: `85 + d * (1 ±0)` 116 | // Estimated: `3528` 117 | // Minimum execution time: 10_000_000 picoseconds. 118 | Weight::from_parts(10_548_865, 3528) 119 | // Standard Error: 15_304 120 | .saturating_add(Weight::from_parts(27_459, 0).saturating_mul(d.into())) 121 | .saturating_add(RocksDbWeight::get().reads(1_u64)) 122 | .saturating_add(RocksDbWeight::get().writes(1_u64)) 123 | } 124 | /// Storage: `PoeModule::Proofs` (r:1 w:1) 125 | /// Proof: `PoeModule::Proofs` (`max_values`: None, `max_size`: Some(63), added: 2538, mode: `MaxEncodedLen`) 126 | /// The range of component `d` is `[0, 10]`. 127 | fn transfer_claim(d: u32, ) -> Weight { 128 | // Proof Size summary in bytes: 129 | // Measured: `85 + d * (1 ±0)` 130 | // Estimated: `3528` 131 | // Minimum execution time: 7_000_000 picoseconds. 132 | Weight::from_parts(7_716_488, 3528) 133 | // Standard Error: 10_996 134 | .saturating_add(Weight::from_parts(31_472, 0).saturating_mul(d.into())) 135 | .saturating_add(RocksDbWeight::get().reads(1_u64)) 136 | .saturating_add(RocksDbWeight::get().writes(1_u64)) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /pallets/template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pallet-template" 3 | version = "4.0.0-dev" 4 | description = "FRAME pallet template for defining custom runtime logic." 5 | authors = ["Substrate DevHub "] 6 | homepage = "https://substrate.io" 7 | edition = "2021" 8 | license = "Unlicense" 9 | publish = false 10 | repository = "https://github.com/substrate-developer-hub/substrate-node-template/" 11 | 12 | [package.metadata.docs.rs] 13 | targets = ["x86_64-unknown-linux-gnu"] 14 | 15 | [dependencies] 16 | codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ 17 | "derive", 18 | ] } 19 | scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } 20 | frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 21 | frame-support = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 22 | frame-system = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 23 | sp-std = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 24 | 25 | [dev-dependencies] 26 | sp-core = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 27 | sp-io = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 28 | sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 29 | 30 | [features] 31 | default = ["std"] 32 | std = [ 33 | "codec/std", 34 | "frame-benchmarking?/std", 35 | "frame-support/std", 36 | "frame-system/std", 37 | "scale-info/std", 38 | "sp-std/std", 39 | ] 40 | runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] 41 | try-runtime = ["frame-support/try-runtime"] 42 | -------------------------------------------------------------------------------- /pallets/template/expanded.rs: -------------------------------------------------------------------------------- 1 | #![feature(prelude_import)] 2 | #[prelude_import] 3 | use std::prelude::v1::*; 4 | #[macro_use] 5 | extern crate std; 6 | /// A FRAME pallet template with necessary imports 7 | /// Feel free to remove or edit this file as needed. 8 | /// If you change the name of this file, make sure to update its references in runtime/src/lib.rs 9 | /// If you remove this file, you can remove those references 10 | /// For more guidance on Substrate FRAME, see the example pallet 11 | /// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs 12 | use frame_support::{decl_module, decl_storage, decl_event, decl_error, dispatch}; 13 | use frame_system::{self as system, ensure_signed}; 14 | /// The pallet's configuration trait. 15 | pub trait Trait: system::Trait { 16 | /// The overarching event type. 17 | type Event: From> + Into<::Event>; 18 | } 19 | use self::sp_api_hidden_includes_decl_storage::hidden_include::{ 20 | StorageValue as _, StorageMap as _, StorageDoubleMap as _, StoragePrefixedMap as _, 21 | }; 22 | #[doc(hidden)] 23 | mod sp_api_hidden_includes_decl_storage { 24 | pub extern crate frame_support as hidden_include; 25 | } 26 | trait Store { 27 | type Something; 28 | } 29 | impl Store for Module { 30 | type Something = Something; 31 | } 32 | impl Module { 33 | pub fn something() -> Option { 34 | < Something < > as self :: sp_api_hidden_includes_decl_storage :: hidden_include :: storage :: StorageValue < u32 > > :: get ( ) 35 | } 36 | } 37 | #[doc(hidden)] 38 | pub struct __GetByteStructSomething( 39 | pub self::sp_api_hidden_includes_decl_storage::hidden_include::sp_std::marker::PhantomData<(T)>, 40 | ); 41 | #[cfg(feature = "std")] 42 | #[allow(non_upper_case_globals)] 43 | static __CACHE_GET_BYTE_STRUCT_Something: 44 | self::sp_api_hidden_includes_decl_storage::hidden_include::once_cell::sync::OnceCell< 45 | self::sp_api_hidden_includes_decl_storage::hidden_include::sp_std::vec::Vec, 46 | > = self::sp_api_hidden_includes_decl_storage::hidden_include::once_cell::sync::OnceCell::new(); 47 | #[cfg(feature = "std")] 48 | impl self::sp_api_hidden_includes_decl_storage::hidden_include::metadata::DefaultByte 49 | for __GetByteStructSomething 50 | { 51 | fn default_byte( 52 | &self, 53 | ) -> self::sp_api_hidden_includes_decl_storage::hidden_include::sp_std::vec::Vec { 54 | use self::sp_api_hidden_includes_decl_storage::hidden_include::codec::Encode; 55 | __CACHE_GET_BYTE_STRUCT_Something 56 | .get_or_init(|| { 57 | let def_val: Option = Default::default(); 58 | as Encode>::encode(&def_val) 59 | }) 60 | .clone() 61 | } 62 | } 63 | unsafe impl Send for __GetByteStructSomething {} 64 | unsafe impl Sync for __GetByteStructSomething {} 65 | impl Module { 66 | #[doc(hidden)] 67 | pub fn storage_metadata( 68 | ) -> self::sp_api_hidden_includes_decl_storage::hidden_include::metadata::StorageMetadata { 69 | self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: StorageMetadata { prefix : self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: DecodeDifferent :: Encode ( "TemplateModule" ) , entries : self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: DecodeDifferent :: Encode ( & [ self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: StorageEntryMetadata { name : self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: DecodeDifferent :: Encode ( "Something" ) , modifier : self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: StorageEntryModifier :: Optional , ty : self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: StorageEntryType :: Plain ( self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: DecodeDifferent :: Encode ( "u32" ) ) , default : self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: DecodeDifferent :: Encode ( self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: DefaultByteGetter ( & __GetByteStructSomething :: < T > ( self :: sp_api_hidden_includes_decl_storage :: hidden_include :: sp_std :: marker :: PhantomData ) ) ) , documentation : self :: sp_api_hidden_includes_decl_storage :: hidden_include :: metadata :: DecodeDifferent :: Encode ( & [ ] ) , } ] [ .. ] ) , } 70 | } 71 | } 72 | /// Tag a type as an instance of a module. 73 | /// 74 | /// Defines storage prefixes, they must be unique. 75 | #[doc(hidden)] 76 | pub trait __GeneratedInstantiable: 'static { 77 | /// The prefix used by any storage entry of an instance. 78 | const PREFIX: &'static str; 79 | } 80 | #[doc(hidden)] 81 | pub struct __InherentHiddenInstance; 82 | #[automatically_derived] 83 | #[allow(unused_qualifications)] 84 | impl ::core::clone::Clone for __InherentHiddenInstance { 85 | #[inline] 86 | fn clone(&self) -> __InherentHiddenInstance { 87 | match *self { 88 | __InherentHiddenInstance => __InherentHiddenInstance, 89 | } 90 | } 91 | } 92 | impl ::core::marker::StructuralEq for __InherentHiddenInstance {} 93 | #[automatically_derived] 94 | #[allow(unused_qualifications)] 95 | impl ::core::cmp::Eq for __InherentHiddenInstance { 96 | #[inline] 97 | #[doc(hidden)] 98 | fn assert_receiver_is_total_eq(&self) -> () { 99 | {} 100 | } 101 | } 102 | impl ::core::marker::StructuralPartialEq for __InherentHiddenInstance {} 103 | #[automatically_derived] 104 | #[allow(unused_qualifications)] 105 | impl ::core::cmp::PartialEq for __InherentHiddenInstance { 106 | #[inline] 107 | fn eq(&self, other: &__InherentHiddenInstance) -> bool { 108 | match *other { 109 | __InherentHiddenInstance => match *self { 110 | __InherentHiddenInstance => true, 111 | }, 112 | } 113 | } 114 | } 115 | const _: () = { 116 | #[allow(unknown_lints)] 117 | #[allow(rust_2018_idioms)] 118 | extern crate codec as _parity_scale_codec; 119 | impl _parity_scale_codec::Encode for __InherentHiddenInstance { 120 | fn encode_to(&self, dest: &mut EncOut) {} 121 | } 122 | impl _parity_scale_codec::EncodeLike for __InherentHiddenInstance {} 123 | }; 124 | const _: () = { 125 | #[allow(unknown_lints)] 126 | #[allow(rust_2018_idioms)] 127 | extern crate codec as _parity_scale_codec; 128 | impl _parity_scale_codec::Decode for __InherentHiddenInstance { 129 | fn decode( 130 | input: &mut DecIn, 131 | ) -> core::result::Result { 132 | Ok(__InherentHiddenInstance) 133 | } 134 | } 135 | }; 136 | impl core::fmt::Debug for __InherentHiddenInstance { 137 | fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { 138 | fmt.debug_tuple("__InherentHiddenInstance").finish() 139 | } 140 | } 141 | impl __GeneratedInstantiable for __InherentHiddenInstance { 142 | const PREFIX: &'static str = "TemplateModule"; 143 | } 144 | struct Something( 145 | self::sp_api_hidden_includes_decl_storage::hidden_include::sp_std::marker::PhantomData<()>, 146 | ); 147 | impl 148 | self::sp_api_hidden_includes_decl_storage::hidden_include::storage::generator::StorageValue 149 | for Something 150 | { 151 | type Query = Option; 152 | fn module_prefix() -> &'static [u8] { 153 | __InherentHiddenInstance::PREFIX.as_bytes() 154 | } 155 | fn storage_prefix() -> &'static [u8] { 156 | "Something".as_bytes() 157 | } 158 | fn from_optional_value_to_query(v: Option) -> Self::Query { 159 | v.or_else(|| Default::default()) 160 | } 161 | fn from_query_to_optional_value(v: Self::Query) -> Option { 162 | v 163 | } 164 | } 165 | /// [`RawEvent`] specialized for the configuration [`Trait`] 166 | /// 167 | /// [`RawEvent`]: enum.RawEvent.html 168 | /// [`Trait`]: trait.Trait.html 169 | pub type Event = RawEvent<::AccountId>; 170 | /// Events for this module. 171 | /// 172 | pub enum RawEvent { 173 | /// Just a dummy event. 174 | /// Event `Something` is declared with a parameter of the type `u32` and `AccountId` 175 | /// To emit this event, we call the deposit function, from our runtime functions 176 | SomethingStored(u32, AccountId), 177 | } 178 | #[automatically_derived] 179 | #[allow(unused_qualifications)] 180 | impl ::core::clone::Clone for RawEvent { 181 | #[inline] 182 | fn clone(&self) -> RawEvent { 183 | match (&*self,) { 184 | (&RawEvent::SomethingStored(ref __self_0, ref __self_1),) => RawEvent::SomethingStored( 185 | ::core::clone::Clone::clone(&(*__self_0)), 186 | ::core::clone::Clone::clone(&(*__self_1)), 187 | ), 188 | } 189 | } 190 | } 191 | impl ::core::marker::StructuralPartialEq for RawEvent {} 192 | #[automatically_derived] 193 | #[allow(unused_qualifications)] 194 | impl ::core::cmp::PartialEq for RawEvent { 195 | #[inline] 196 | fn eq(&self, other: &RawEvent) -> bool { 197 | match (&*self, &*other) { 198 | ( 199 | &RawEvent::SomethingStored(ref __self_0, ref __self_1), 200 | &RawEvent::SomethingStored(ref __arg_1_0, ref __arg_1_1), 201 | ) => (*__self_0) == (*__arg_1_0) && (*__self_1) == (*__arg_1_1), 202 | } 203 | } 204 | #[inline] 205 | fn ne(&self, other: &RawEvent) -> bool { 206 | match (&*self, &*other) { 207 | ( 208 | &RawEvent::SomethingStored(ref __self_0, ref __self_1), 209 | &RawEvent::SomethingStored(ref __arg_1_0, ref __arg_1_1), 210 | ) => (*__self_0) != (*__arg_1_0) || (*__self_1) != (*__arg_1_1), 211 | } 212 | } 213 | } 214 | impl ::core::marker::StructuralEq for RawEvent {} 215 | #[automatically_derived] 216 | #[allow(unused_qualifications)] 217 | impl ::core::cmp::Eq for RawEvent { 218 | #[inline] 219 | #[doc(hidden)] 220 | fn assert_receiver_is_total_eq(&self) -> () { 221 | { 222 | let _: ::core::cmp::AssertParamIsEq; 223 | let _: ::core::cmp::AssertParamIsEq; 224 | } 225 | } 226 | } 227 | const _: () = { 228 | #[allow(unknown_lints)] 229 | #[allow(rust_2018_idioms)] 230 | extern crate codec as _parity_scale_codec; 231 | impl _parity_scale_codec::Encode for RawEvent 232 | where 233 | AccountId: _parity_scale_codec::Encode, 234 | AccountId: _parity_scale_codec::Encode, 235 | { 236 | fn encode_to(&self, dest: &mut EncOut) { 237 | match *self { 238 | RawEvent::SomethingStored(ref aa, ref ba) => { 239 | dest.push_byte(0usize as u8); 240 | dest.push(aa); 241 | dest.push(ba); 242 | } 243 | _ => (), 244 | } 245 | } 246 | } 247 | impl _parity_scale_codec::EncodeLike for RawEvent 248 | where 249 | AccountId: _parity_scale_codec::Encode, 250 | AccountId: _parity_scale_codec::Encode, 251 | { 252 | } 253 | }; 254 | const _: () = { 255 | #[allow(unknown_lints)] 256 | #[allow(rust_2018_idioms)] 257 | extern crate codec as _parity_scale_codec; 258 | impl _parity_scale_codec::Decode for RawEvent 259 | where 260 | AccountId: _parity_scale_codec::Decode, 261 | AccountId: _parity_scale_codec::Decode, 262 | { 263 | fn decode( 264 | input: &mut DecIn, 265 | ) -> core::result::Result { 266 | match input.read_byte()? { 267 | x if x == 0usize as u8 => Ok(RawEvent::SomethingStored( 268 | { 269 | let res = _parity_scale_codec::Decode::decode(input); 270 | match res { 271 | Err(_) => { 272 | return Err( 273 | "Error decoding field RawEvent :: SomethingStored.0".into() 274 | ) 275 | } 276 | Ok(a) => a, 277 | } 278 | }, 279 | { 280 | let res = _parity_scale_codec::Decode::decode(input); 281 | match res { 282 | Err(_) => { 283 | return Err( 284 | "Error decoding field RawEvent :: SomethingStored.1".into() 285 | ) 286 | } 287 | Ok(a) => a, 288 | } 289 | }, 290 | )), 291 | x => Err("No such variant in enum RawEvent".into()), 292 | } 293 | } 294 | } 295 | }; 296 | impl core::fmt::Debug for RawEvent 297 | where 298 | AccountId: core::fmt::Debug, 299 | { 300 | fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { 301 | match self { 302 | Self::SomethingStored(ref a0, ref a1) => fmt 303 | .debug_tuple("RawEvent::SomethingStored") 304 | .field(a0) 305 | .field(a1) 306 | .finish(), 307 | _ => Ok(()), 308 | } 309 | } 310 | } 311 | impl From> for () { 312 | fn from(_: RawEvent) -> () { 313 | () 314 | } 315 | } 316 | impl RawEvent { 317 | #[allow(dead_code)] 318 | #[doc(hidden)] 319 | pub fn metadata() -> &'static [::frame_support::event::EventMetadata] { 320 | &[::frame_support::event::EventMetadata { 321 | name: ::frame_support::event::DecodeDifferent::Encode("SomethingStored"), 322 | arguments: ::frame_support::event::DecodeDifferent::Encode(&["u32", "AccountId"]), 323 | documentation: ::frame_support::event::DecodeDifferent::Encode(&[ 324 | r" Just a dummy event.", 325 | r" Event `Something` is declared with a parameter of the type `u32` and `AccountId`", 326 | r" To emit this event, we call the deposit function, from our runtime functions", 327 | ]), 328 | }] 329 | } 330 | } 331 | pub enum Error { 332 | #[doc(hidden)] 333 | __Ignore( 334 | ::frame_support::sp_std::marker::PhantomData<(T,)>, 335 | ::frame_support::Never, 336 | ), 337 | /// Value was None 338 | NoneValue, 339 | /// Value reached maximum and cannot be incremented further 340 | StorageOverflow, 341 | } 342 | impl ::frame_support::sp_std::fmt::Debug for Error { 343 | fn fmt( 344 | &self, 345 | f: &mut ::frame_support::sp_std::fmt::Formatter<'_>, 346 | ) -> ::frame_support::sp_std::fmt::Result { 347 | f.write_str(self.as_str()) 348 | } 349 | } 350 | impl Error { 351 | fn as_u8(&self) -> u8 { 352 | match self { 353 | Error::__Ignore(_, _) => ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1( 354 | &["internal error: entered unreachable code: "], 355 | &match (&"`__Ignore` can never be constructed",) { 356 | (arg0,) => [::core::fmt::ArgumentV1::new( 357 | arg0, 358 | ::core::fmt::Display::fmt, 359 | )], 360 | }, 361 | )), 362 | Error::NoneValue => 0, 363 | Error::StorageOverflow => 0 + 1, 364 | } 365 | } 366 | fn as_str(&self) -> &'static str { 367 | match self { 368 | Self::__Ignore(_, _) => ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1( 369 | &["internal error: entered unreachable code: "], 370 | &match (&"`__Ignore` can never be constructed",) { 371 | (arg0,) => [::core::fmt::ArgumentV1::new( 372 | arg0, 373 | ::core::fmt::Display::fmt, 374 | )], 375 | }, 376 | )), 377 | Error::NoneValue => "NoneValue", 378 | Error::StorageOverflow => "StorageOverflow", 379 | } 380 | } 381 | } 382 | impl From> for &'static str { 383 | fn from(err: Error) -> &'static str { 384 | err.as_str() 385 | } 386 | } 387 | impl From> for ::frame_support::sp_runtime::DispatchError { 388 | fn from(err: Error) -> Self { 389 | let index = ::module_to_index::< 390 | Module, 391 | >() 392 | .expect("Every active module has an index in the runtime; qed") as u8; 393 | ::frame_support::sp_runtime::DispatchError::Module { 394 | index, 395 | error: err.as_u8(), 396 | message: Some(err.as_str()), 397 | } 398 | } 399 | } 400 | impl ::frame_support::error::ModuleErrorMetadata for Error { 401 | fn metadata() -> &'static [::frame_support::error::ErrorMetadata] { 402 | &[ 403 | ::frame_support::error::ErrorMetadata { 404 | name: ::frame_support::error::DecodeDifferent::Encode("NoneValue"), 405 | documentation: ::frame_support::error::DecodeDifferent::Encode(&[ 406 | r" Value was None", 407 | ]), 408 | }, 409 | ::frame_support::error::ErrorMetadata { 410 | name: ::frame_support::error::DecodeDifferent::Encode("StorageOverflow"), 411 | documentation: ::frame_support::error::DecodeDifferent::Encode(&[ 412 | r" Value reached maximum and cannot be incremented further", 413 | ]), 414 | }, 415 | ] 416 | } 417 | } 418 | /// The module declaration. 419 | pub struct Module(::frame_support::sp_std::marker::PhantomData<(T,)>); 420 | #[automatically_derived] 421 | #[allow(unused_qualifications)] 422 | impl ::core::clone::Clone for Module { 423 | #[inline] 424 | fn clone(&self) -> Module { 425 | match *self { 426 | Module(ref __self_0_0) => Module(::core::clone::Clone::clone(&(*__self_0_0))), 427 | } 428 | } 429 | } 430 | #[automatically_derived] 431 | #[allow(unused_qualifications)] 432 | impl ::core::marker::Copy for Module {} 433 | impl ::core::marker::StructuralPartialEq for Module {} 434 | #[automatically_derived] 435 | #[allow(unused_qualifications)] 436 | impl ::core::cmp::PartialEq for Module { 437 | #[inline] 438 | fn eq(&self, other: &Module) -> bool { 439 | match *other { 440 | Module(ref __self_1_0) => match *self { 441 | Module(ref __self_0_0) => (*__self_0_0) == (*__self_1_0), 442 | }, 443 | } 444 | } 445 | #[inline] 446 | fn ne(&self, other: &Module) -> bool { 447 | match *other { 448 | Module(ref __self_1_0) => match *self { 449 | Module(ref __self_0_0) => (*__self_0_0) != (*__self_1_0), 450 | }, 451 | } 452 | } 453 | } 454 | impl ::core::marker::StructuralEq for Module {} 455 | #[automatically_derived] 456 | #[allow(unused_qualifications)] 457 | impl ::core::cmp::Eq for Module { 458 | #[inline] 459 | #[doc(hidden)] 460 | fn assert_receiver_is_total_eq(&self) -> () { 461 | { 462 | let _: ::core::cmp::AssertParamIsEq< 463 | ::frame_support::sp_std::marker::PhantomData<(T,)>, 464 | >; 465 | } 466 | } 467 | } 468 | impl core::fmt::Debug for Module 469 | where 470 | T: core::fmt::Debug, 471 | { 472 | fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { 473 | fmt.debug_tuple("Module").field(&self.0).finish() 474 | } 475 | } 476 | impl ::frame_support::traits::OnInitialize for Module {} 477 | impl ::frame_support::traits::OnRuntimeUpgrade for Module {} 478 | impl ::frame_support::traits::OnFinalize for Module {} 479 | impl ::frame_support::traits::OffchainWorker for Module {} 480 | impl Module { 481 | fn deposit_event(event: impl Into<::Event>) { 482 | >::deposit_event(event.into()) 483 | } 484 | } 485 | /// Can also be called using [`Call`]. 486 | /// 487 | /// [`Call`]: enum.Call.html 488 | impl Module { 489 | /// Just a dummy entry point. 490 | /// function that can be called by the external world as an extrinsics call 491 | /// takes a parameter of the type `AccountId`, stores it, and emits an event 492 | pub fn do_something(origin: T::Origin, something: u32) -> dispatch::DispatchResult { 493 | let __tracing_span__ = { 494 | { 495 | if ::tracing::dispatcher::has_been_set() 496 | && ::sp_tracing::tracing::Level::TRACE 497 | <= ::tracing::level_filters::STATIC_MAX_LEVEL 498 | { 499 | use ::tracing::callsite; 500 | use ::tracing::callsite::Callsite; 501 | let callsite = { 502 | use ::tracing::{callsite, subscriber::Interest, Metadata, __macro_support::*}; 503 | struct MyCallsite; 504 | static META: Metadata<'static> = { 505 | ::tracing_core::metadata::Metadata::new( 506 | "do_something", 507 | "pallet_template", 508 | ::sp_tracing::tracing::Level::TRACE, 509 | Some("pallets/template/src/lib.rs"), 510 | Some(63u32), 511 | Some("pallet_template"), 512 | ::tracing_core::field::FieldSet::new( 513 | &[], 514 | ::tracing_core::callsite::Identifier(&MyCallsite), 515 | ), 516 | ::tracing::metadata::Kind::SPAN, 517 | ) 518 | }; 519 | static INTEREST: AtomicUsize = AtomicUsize::new(0); 520 | static REGISTRATION: Once = Once::new(); 521 | impl MyCallsite { 522 | #[inline] 523 | fn interest(&self) -> Interest { 524 | match INTEREST.load(Ordering::Relaxed) { 525 | 0 => Interest::never(), 526 | 2 => Interest::always(), 527 | _ => Interest::sometimes(), 528 | } 529 | } 530 | } 531 | impl callsite::Callsite for MyCallsite { 532 | fn set_interest(&self, interest: Interest) { 533 | let interest = match () { 534 | _ if interest.is_never() => 0, 535 | _ if interest.is_always() => 2, 536 | _ => 1, 537 | }; 538 | INTEREST.store(interest, Ordering::SeqCst); 539 | } 540 | fn metadata(&self) -> &Metadata { 541 | &META 542 | } 543 | } 544 | REGISTRATION.call_once(|| { 545 | callsite::register(&MyCallsite); 546 | }); 547 | &MyCallsite 548 | }; 549 | let meta = callsite.metadata(); 550 | if { 551 | let interest = callsite.interest(); 552 | if interest.is_never() { 553 | false 554 | } else if interest.is_always() { 555 | true 556 | } else { 557 | let meta = callsite.metadata(); 558 | ::tracing::dispatcher::get_default(|current| current.enabled(meta)) 559 | } 560 | } { 561 | ::tracing::Span::new(meta, &{ meta.fields().value_set(&[]) }) 562 | } else { 563 | ::tracing::Span::none() 564 | } 565 | } else { 566 | ::tracing::Span::none() 567 | } 568 | } 569 | }; 570 | let __tracing_guard__ = { __tracing_span__.enter() }; 571 | let who = ensure_signed(origin)?; 572 | Something::put(something); 573 | Self::deposit_event(RawEvent::SomethingStored(something, who)); 574 | Ok(()) 575 | } 576 | /// Another dummy entry point. 577 | /// takes no parameters, attempts to increment storage value, and possibly throws an error 578 | pub fn cause_error(origin: T::Origin) -> dispatch::DispatchResult { 579 | let __tracing_span__ = { 580 | { 581 | if ::tracing::dispatcher::has_been_set() 582 | && ::sp_tracing::tracing::Level::TRACE 583 | <= ::tracing::level_filters::STATIC_MAX_LEVEL 584 | { 585 | use ::tracing::callsite; 586 | use ::tracing::callsite::Callsite; 587 | let callsite = { 588 | use ::tracing::{callsite, subscriber::Interest, Metadata, __macro_support::*}; 589 | struct MyCallsite; 590 | static META: Metadata<'static> = { 591 | ::tracing_core::metadata::Metadata::new( 592 | "cause_error", 593 | "pallet_template", 594 | ::sp_tracing::tracing::Level::TRACE, 595 | Some("pallets/template/src/lib.rs"), 596 | Some(63u32), 597 | Some("pallet_template"), 598 | ::tracing_core::field::FieldSet::new( 599 | &[], 600 | ::tracing_core::callsite::Identifier(&MyCallsite), 601 | ), 602 | ::tracing::metadata::Kind::SPAN, 603 | ) 604 | }; 605 | static INTEREST: AtomicUsize = AtomicUsize::new(0); 606 | static REGISTRATION: Once = Once::new(); 607 | impl MyCallsite { 608 | #[inline] 609 | fn interest(&self) -> Interest { 610 | match INTEREST.load(Ordering::Relaxed) { 611 | 0 => Interest::never(), 612 | 2 => Interest::always(), 613 | _ => Interest::sometimes(), 614 | } 615 | } 616 | } 617 | impl callsite::Callsite for MyCallsite { 618 | fn set_interest(&self, interest: Interest) { 619 | let interest = match () { 620 | _ if interest.is_never() => 0, 621 | _ if interest.is_always() => 2, 622 | _ => 1, 623 | }; 624 | INTEREST.store(interest, Ordering::SeqCst); 625 | } 626 | fn metadata(&self) -> &Metadata { 627 | &META 628 | } 629 | } 630 | REGISTRATION.call_once(|| { 631 | callsite::register(&MyCallsite); 632 | }); 633 | &MyCallsite 634 | }; 635 | let meta = callsite.metadata(); 636 | if { 637 | let interest = callsite.interest(); 638 | if interest.is_never() { 639 | false 640 | } else if interest.is_always() { 641 | true 642 | } else { 643 | let meta = callsite.metadata(); 644 | ::tracing::dispatcher::get_default(|current| current.enabled(meta)) 645 | } 646 | } { 647 | ::tracing::Span::new(meta, &{ meta.fields().value_set(&[]) }) 648 | } else { 649 | ::tracing::Span::none() 650 | } 651 | } else { 652 | ::tracing::Span::none() 653 | } 654 | } 655 | }; 656 | let __tracing_guard__ = { __tracing_span__.enter() }; 657 | let _who = ensure_signed(origin)?; 658 | match Something::get() { 659 | None => Err(Error::::NoneValue)?, 660 | Some(old) => { 661 | let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; 662 | Something::put(new); 663 | Ok(()) 664 | } 665 | } 666 | } 667 | } 668 | /// Dispatchable calls. 669 | /// 670 | /// Each variant of this enum maps to a dispatchable function from the associated module. 671 | pub enum Call { 672 | #[doc(hidden)] 673 | #[codec(skip)] 674 | __PhantomItem( 675 | ::frame_support::sp_std::marker::PhantomData<(T,)>, 676 | ::frame_support::Never, 677 | ), 678 | #[allow(non_camel_case_types)] 679 | /// Just a dummy entry point. 680 | /// function that can be called by the external world as an extrinsics call 681 | /// takes a parameter of the type `AccountId`, stores it, and emits an event 682 | do_something(u32), 683 | #[allow(non_camel_case_types)] 684 | /// Another dummy entry point. 685 | /// takes no parameters, attempts to increment storage value, and possibly throws an error 686 | cause_error(), 687 | } 688 | const _: () = { 689 | #[allow(unknown_lints)] 690 | #[allow(rust_2018_idioms)] 691 | extern crate codec as _parity_scale_codec; 692 | impl _parity_scale_codec::Encode for Call { 693 | fn encode_to(&self, dest: &mut EncOut) { 694 | match *self { 695 | Call::do_something(ref aa) => { 696 | dest.push_byte(0usize as u8); 697 | dest.push(aa); 698 | } 699 | Call::cause_error() => { 700 | dest.push_byte(1usize as u8); 701 | } 702 | _ => (), 703 | } 704 | } 705 | } 706 | impl _parity_scale_codec::EncodeLike for Call {} 707 | }; 708 | const _: () = { 709 | #[allow(unknown_lints)] 710 | #[allow(rust_2018_idioms)] 711 | extern crate codec as _parity_scale_codec; 712 | impl _parity_scale_codec::Decode for Call { 713 | fn decode( 714 | input: &mut DecIn, 715 | ) -> core::result::Result { 716 | match input.read_byte()? { 717 | x if x == 0usize as u8 => Ok(Call::do_something({ 718 | let res = _parity_scale_codec::Decode::decode(input); 719 | match res { 720 | Err(_) => return Err("Error decoding field Call :: do_something.0".into()), 721 | Ok(a) => a, 722 | } 723 | })), 724 | x if x == 1usize as u8 => Ok(Call::cause_error()), 725 | x => Err("No such variant in enum Call".into()), 726 | } 727 | } 728 | } 729 | }; 730 | impl ::frame_support::dispatch::GetDispatchInfo for Call { 731 | fn get_dispatch_info(&self) -> ::frame_support::dispatch::DispatchInfo { 732 | match *self { 733 | Call::do_something(ref something) => { 734 | let base_weight = 10_000; 735 | let weight = >::weigh_data( 736 | &base_weight, 737 | (something,), 738 | ); 739 | let class = 740 | >::classify_dispatch( 741 | &base_weight, 742 | (something,), 743 | ); 744 | let pays_fee = >::pays_fee( 745 | &base_weight, 746 | (something,), 747 | ); 748 | ::frame_support::dispatch::DispatchInfo { 749 | weight, 750 | class, 751 | pays_fee, 752 | } 753 | } 754 | Call::cause_error() => { 755 | let base_weight = 10_000; 756 | let weight = 757 | >::weigh_data(&base_weight, ()); 758 | let class = 759 | >::classify_dispatch( 760 | &base_weight, 761 | (), 762 | ); 763 | let pays_fee = 764 | >::pays_fee(&base_weight, ()); 765 | ::frame_support::dispatch::DispatchInfo { 766 | weight, 767 | class, 768 | pays_fee, 769 | } 770 | } 771 | Call::__PhantomItem(_, _) => { 772 | ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1( 773 | &["internal error: entered unreachable code: "], 774 | &match (&"__PhantomItem should never be used.",) { 775 | (arg0,) => [::core::fmt::ArgumentV1::new( 776 | arg0, 777 | ::core::fmt::Display::fmt, 778 | )], 779 | }, 780 | )) 781 | } 782 | } 783 | } 784 | } 785 | impl ::frame_support::dispatch::GetCallName for Call { 786 | fn get_call_name(&self) -> &'static str { 787 | match *self { 788 | Call::do_something(ref something) => { 789 | let _ = (something); 790 | "do_something" 791 | } 792 | Call::cause_error() => { 793 | let _ = (); 794 | "cause_error" 795 | } 796 | Call::__PhantomItem(_, _) => { 797 | ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1( 798 | &["internal error: entered unreachable code: "], 799 | &match (&"__PhantomItem should never be used.",) { 800 | (arg0,) => [::core::fmt::ArgumentV1::new( 801 | arg0, 802 | ::core::fmt::Display::fmt, 803 | )], 804 | }, 805 | )) 806 | } 807 | } 808 | } 809 | fn get_call_names() -> &'static [&'static str] { 810 | &["do_something", "cause_error"] 811 | } 812 | } 813 | impl ::frame_support::dispatch::Clone for Call { 814 | fn clone(&self) -> Self { 815 | match *self { 816 | Call::do_something(ref something) => Call::do_something((*something).clone()), 817 | Call::cause_error() => Call::cause_error(), 818 | _ => ::std::rt::begin_panic("internal error: entered unreachable code"), 819 | } 820 | } 821 | } 822 | impl ::frame_support::dispatch::PartialEq for Call { 823 | fn eq(&self, _other: &Self) -> bool { 824 | match *self { 825 | Call::do_something(ref something) => { 826 | let self_params = (something,); 827 | if let Call::do_something(ref something) = *_other { 828 | self_params == (something,) 829 | } else { 830 | match *_other { 831 | Call::__PhantomItem(_, _) => { 832 | ::std::rt::begin_panic("internal error: entered unreachable code") 833 | } 834 | _ => false, 835 | } 836 | } 837 | } 838 | Call::cause_error() => { 839 | let self_params = (); 840 | if let Call::cause_error() = *_other { 841 | self_params == () 842 | } else { 843 | match *_other { 844 | Call::__PhantomItem(_, _) => { 845 | ::std::rt::begin_panic("internal error: entered unreachable code") 846 | } 847 | _ => false, 848 | } 849 | } 850 | } 851 | _ => ::std::rt::begin_panic("internal error: entered unreachable code"), 852 | } 853 | } 854 | } 855 | impl ::frame_support::dispatch::Eq for Call {} 856 | impl ::frame_support::dispatch::fmt::Debug for Call { 857 | fn fmt( 858 | &self, 859 | _f: &mut ::frame_support::dispatch::fmt::Formatter, 860 | ) -> ::frame_support::dispatch::result::Result<(), ::frame_support::dispatch::fmt::Error> { 861 | match *self { 862 | Call::do_something(ref something) => _f.write_fmt(::core::fmt::Arguments::new_v1( 863 | &["", ""], 864 | &match (&"do_something", &(something.clone(),)) { 865 | (arg0, arg1) => [ 866 | ::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Display::fmt), 867 | ::core::fmt::ArgumentV1::new(arg1, ::core::fmt::Debug::fmt), 868 | ], 869 | }, 870 | )), 871 | Call::cause_error() => _f.write_fmt(::core::fmt::Arguments::new_v1( 872 | &["", ""], 873 | &match (&"cause_error", &()) { 874 | (arg0, arg1) => [ 875 | ::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Display::fmt), 876 | ::core::fmt::ArgumentV1::new(arg1, ::core::fmt::Debug::fmt), 877 | ], 878 | }, 879 | )), 880 | _ => ::std::rt::begin_panic("internal error: entered unreachable code"), 881 | } 882 | } 883 | } 884 | impl ::frame_support::dispatch::Dispatchable for Call { 885 | type Trait = T; 886 | type Origin = T::Origin; 887 | type Info = ::frame_support::weights::DispatchInfo; 888 | type PostInfo = ::frame_support::weights::PostDispatchInfo; 889 | fn dispatch( 890 | self, 891 | _origin: Self::Origin, 892 | ) -> ::frame_support::dispatch::DispatchResultWithPostInfo { 893 | match self { 894 | Call::do_something(something) => >::do_something(_origin, something) 895 | .map(Into::into) 896 | .map_err(Into::into), 897 | Call::cause_error() => >::cause_error(_origin) 898 | .map(Into::into) 899 | .map_err(Into::into), 900 | Call::__PhantomItem(_, _) => { 901 | ::std::rt::begin_panic_fmt(&::core::fmt::Arguments::new_v1( 902 | &["internal error: entered unreachable code: "], 903 | &match (&"__PhantomItem should never be used.",) { 904 | (arg0,) => [::core::fmt::ArgumentV1::new( 905 | arg0, 906 | ::core::fmt::Display::fmt, 907 | )], 908 | }, 909 | )) 910 | } 911 | } 912 | } 913 | } 914 | impl ::frame_support::dispatch::Callable for Module { 915 | type Call = Call; 916 | } 917 | impl Module { 918 | #[doc(hidden)] 919 | pub fn dispatch< 920 | D: ::frame_support::dispatch::Dispatchable< 921 | Trait = T, 922 | PostInfo = ::frame_support::weights::PostDispatchInfo, 923 | >, 924 | >( 925 | d: D, 926 | origin: D::Origin, 927 | ) -> ::frame_support::dispatch::DispatchResultWithPostInfo { 928 | d.dispatch(origin) 929 | } 930 | } 931 | impl Module { 932 | #[doc(hidden)] 933 | pub fn call_functions() -> &'static [::frame_support::dispatch::FunctionMetadata] { 934 | &[ 935 | ::frame_support::dispatch::FunctionMetadata { 936 | name: ::frame_support::dispatch::DecodeDifferent::Encode("do_something"), 937 | arguments: ::frame_support::dispatch::DecodeDifferent::Encode(&[ 938 | ::frame_support::dispatch::FunctionArgumentMetadata { 939 | name: ::frame_support::dispatch::DecodeDifferent::Encode("something"), 940 | ty: ::frame_support::dispatch::DecodeDifferent::Encode("u32"), 941 | }, 942 | ]), 943 | documentation: ::frame_support::dispatch::DecodeDifferent::Encode(&[ 944 | r" Just a dummy entry point.", 945 | r" function that can be called by the external world as an extrinsics call", 946 | r" takes a parameter of the type `AccountId`, stores it, and emits an event", 947 | ]), 948 | }, 949 | ::frame_support::dispatch::FunctionMetadata { 950 | name: ::frame_support::dispatch::DecodeDifferent::Encode("cause_error"), 951 | arguments: ::frame_support::dispatch::DecodeDifferent::Encode(&[]), 952 | documentation: ::frame_support::dispatch::DecodeDifferent::Encode(&[ 953 | r" Another dummy entry point.", 954 | r" takes no parameters, attempts to increment storage value, and possibly throws an error", 955 | ]), 956 | }, 957 | ] 958 | } 959 | } 960 | impl Module { 961 | #[doc(hidden)] 962 | pub fn module_constants_metadata( 963 | ) -> &'static [::frame_support::dispatch::ModuleConstantMetadata] { 964 | &[] 965 | } 966 | } 967 | impl ::frame_support::dispatch::ModuleErrorMetadata for Module { 968 | fn metadata() -> &'static [::frame_support::dispatch::ErrorMetadata] { 969 | as ::frame_support::dispatch::ModuleErrorMetadata>::metadata() 970 | } 971 | } 972 | -------------------------------------------------------------------------------- /pallets/template/src/benchmarking.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarking setup for pallet-template 2 | #![cfg(feature = "runtime-benchmarks")] 3 | use super::*; 4 | 5 | #[allow(unused)] 6 | use crate::Pallet as Template; 7 | use frame_benchmarking::v2::*; 8 | use frame_system::RawOrigin; 9 | 10 | #[benchmarks] 11 | mod benchmarks { 12 | use super::*; 13 | 14 | #[benchmark] 15 | fn do_something() { 16 | let value = 100u32.into(); 17 | let caller: T::AccountId = whitelisted_caller(); 18 | #[extrinsic_call] 19 | do_something(RawOrigin::Signed(caller), value); 20 | 21 | assert_eq!(Something::::get(), Some(value)); 22 | } 23 | 24 | #[benchmark] 25 | fn cause_error() { 26 | Something::::put(100u32); 27 | let caller: T::AccountId = whitelisted_caller(); 28 | #[extrinsic_call] 29 | cause_error(RawOrigin::Signed(caller)); 30 | 31 | assert_eq!(Something::::get(), Some(101u32)); 32 | } 33 | 34 | impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); 35 | } 36 | -------------------------------------------------------------------------------- /pallets/template/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Template Pallet 2 | //! 3 | //! A pallet with minimal functionality to help developers understand the essential components of 4 | //! writing a FRAME pallet. It is typically used in beginner tutorials or in Substrate template 5 | //! nodes as a starting point for creating a new pallet and **not meant to be used in production**. 6 | //! 7 | //! ## Overview 8 | //! 9 | //! This template pallet contains basic examples of: 10 | //! - declaring a storage item that stores a single `u32` value 11 | //! - declaring and using events 12 | //! - declaring and using errors 13 | //! - a dispatchable function that allows a user to set a new value to storage and emits an event 14 | //! upon success 15 | //! - another dispatchable function that causes a custom error to be thrown 16 | //! 17 | //! Each pallet section is annotated with an attribute using the `#[pallet::...]` procedural macro. 18 | //! This macro generates the necessary code for a pallet to be aggregated into a FRAME runtime. 19 | //! 20 | //! Learn more about FRAME macros [here](https://docs.substrate.io/reference/frame-macros/). 21 | //! 22 | //! ### Pallet Sections 23 | //! 24 | //! The pallet sections in this template are: 25 | //! 26 | //! - A **configuration trait** that defines the types and parameters which the pallet depends on 27 | //! (denoted by the `#[pallet::config]` attribute). See: [`Config`]. 28 | //! - A **means to store pallet-specific data** (denoted by the `#[pallet::storage]` attribute). 29 | //! See: [`storage_types`]. 30 | //! - A **declaration of the events** this pallet emits (denoted by the `#[pallet::event]` 31 | //! attribute). See: [`Event`]. 32 | //! - A **declaration of the errors** that this pallet can throw (denoted by the `#[pallet::error]` 33 | //! attribute). See: [`Error`]. 34 | //! - A **set of dispatchable functions** that define the pallet's functionality (denoted by the 35 | //! `#[pallet::call]` attribute). See: [`dispatchables`]. 36 | //! 37 | //! Run `cargo doc --package pallet-template --open` to view this pallet's documentation. 38 | 39 | // We make sure this pallet uses `no_std` for compiling to Wasm. 40 | #![cfg_attr(not(feature = "std"), no_std)] 41 | 42 | // Re-export pallet items so that they can be accessed from the crate namespace. 43 | pub use pallet::*; 44 | 45 | // FRAME pallets require their own "mock runtimes" to be able to run unit tests. This module 46 | // contains a mock runtime specific for testing this pallet's functionality. 47 | #[cfg(test)] 48 | mod mock; 49 | 50 | // This module contains the unit tests for this pallet. 51 | // Learn about pallet unit testing here: https://docs.substrate.io/test/unit-testing/ 52 | #[cfg(test)] 53 | mod tests; 54 | 55 | // Every callable function or "dispatchable" a pallet exposes must have weight values that correctly 56 | // estimate a dispatchable's execution time. The benchmarking module is used to calculate weights 57 | // for each dispatchable and generates this pallet's weight.rs file. Learn more about benchmarking here: https://docs.substrate.io/test/benchmark/ 58 | #[cfg(feature = "runtime-benchmarks")] 59 | mod benchmarking; 60 | pub mod weights; 61 | pub use weights::*; 62 | 63 | // All pallet logic is defined in its own module and must be annotated by the `pallet` attribute. 64 | #[frame_support::pallet] 65 | pub mod pallet { 66 | // Import various useful types required by all FRAME pallets. 67 | use super::*; 68 | use frame_support::pallet_prelude::*; 69 | use frame_system::pallet_prelude::*; 70 | 71 | // The `Pallet` struct serves as a placeholder to implement traits, methods and dispatchables 72 | // (`Call`s) in this pallet. 73 | #[pallet::pallet] 74 | pub struct Pallet(_); 75 | 76 | /// The pallet's configuration trait. 77 | /// 78 | /// All our types and constants a pallet depends on must be declared here. 79 | /// These types are defined generically and made concrete when the pallet is declared in the 80 | /// `runtime/src/lib.rs` file of your chain. 81 | #[pallet::config] 82 | pub trait Config: frame_system::Config { 83 | /// The overarching runtime event type. 84 | type RuntimeEvent: From> + IsType<::RuntimeEvent>; 85 | /// A type representing the weights required by the dispatchables of this pallet. 86 | type WeightInfo: WeightInfo; 87 | } 88 | 89 | /// A storage item for this pallet. 90 | /// 91 | /// In this template, we are declaring a storage item called `Something` that stores a single 92 | /// `u32` value. Learn more about runtime storage here: 93 | #[pallet::storage] 94 | pub type Something = StorageValue<_, u32>; 95 | 96 | /// Events that functions in this pallet can emit. 97 | /// 98 | /// Events are a simple means of indicating to the outside world (such as dApps, chain explorers 99 | /// or other users) that some notable update in the runtime has occurred. In a FRAME pallet, the 100 | /// documentation for each event field and its parameters is added to a node's metadata so it 101 | /// can be used by external interfaces or tools. 102 | /// 103 | /// The `generate_deposit` macro generates a function on `Pallet` called `deposit_event` which 104 | /// will convert the event type of your pallet into `RuntimeEvent` (declared in the pallet's 105 | /// [`Config`] trait) and deposit it using [`frame_system::Pallet::deposit_event`]. 106 | #[pallet::event] 107 | #[pallet::generate_deposit(pub(super) fn deposit_event)] 108 | pub enum Event { 109 | /// A user has successfully set a new value. 110 | SomethingStored { 111 | /// The new value set. 112 | something: u32, 113 | /// The account who set the new value. 114 | who: T::AccountId, 115 | }, 116 | } 117 | 118 | /// Errors that can be returned by this pallet. 119 | /// 120 | /// Errors tell users that something went wrong so it's important that their naming is 121 | /// informative. Similar to events, error documentation is added to a node's metadata so it's 122 | /// equally important that they have helpful documentation associated with them. 123 | /// 124 | /// This type of runtime error can be up to 4 bytes in size should you want to return additional 125 | /// information. 126 | #[pallet::error] 127 | pub enum Error { 128 | /// The value retrieved was `None` as no value was previously set. 129 | NoneValue, 130 | /// There was an attempt to increment the value in storage over `u32::MAX`. 131 | StorageOverflow, 132 | } 133 | 134 | /// The pallet's dispatchable functions ([`Call`]s). 135 | /// 136 | /// Dispatchable functions allows users to interact with the pallet and invoke state changes. 137 | /// These functions materialize as "extrinsics", which are often compared to transactions. 138 | /// They must always return a `DispatchResult` and be annotated with a weight and call index. 139 | /// 140 | /// The [`call_index`] macro is used to explicitly 141 | /// define an index for calls in the [`Call`] enum. This is useful for pallets that may 142 | /// introduce new dispatchables over time. If the order of a dispatchable changes, its index 143 | /// will also change which will break backwards compatibility. 144 | /// 145 | /// The [`weight`] macro is used to assign a weight to each call. 146 | #[pallet::call] 147 | impl Pallet { 148 | /// An example dispatchable that takes a single u32 value as a parameter, writes the value 149 | /// to storage and emits an event. 150 | /// 151 | /// It checks that the _origin_ for this call is _Signed_ and returns a dispatch 152 | /// error if it isn't. Learn more about origins here: 153 | #[pallet::call_index(0)] 154 | #[pallet::weight(T::WeightInfo::do_something())] 155 | pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { 156 | // Check that the extrinsic was signed and get the signer. 157 | let who = ensure_signed(origin)?; 158 | 159 | // Update storage. 160 | Something::::put(something); 161 | 162 | // Emit an event. 163 | Self::deposit_event(Event::SomethingStored { something, who }); 164 | 165 | // Return a successful `DispatchResult` 166 | Ok(()) 167 | } 168 | 169 | /// An example dispatchable that may throw a custom error. 170 | /// 171 | /// It checks that the caller is a signed origin and reads the current value from the 172 | /// `Something` storage item. If a current value exists, it is incremented by 1 and then 173 | /// written back to storage. 174 | /// 175 | /// ## Errors 176 | /// 177 | /// The function will return an error under the following conditions: 178 | /// 179 | /// - If no value has been set ([`Error::NoneValue`]) 180 | /// - If incrementing the value in storage causes an arithmetic overflow 181 | /// ([`Error::StorageOverflow`]) 182 | #[pallet::call_index(1)] 183 | #[pallet::weight(T::WeightInfo::cause_error())] 184 | pub fn cause_error(origin: OriginFor) -> DispatchResult { 185 | let _who = ensure_signed(origin)?; 186 | 187 | // Read a value from storage. 188 | match Something::::get() { 189 | // Return an error if the value has not been set. 190 | None => Err(Error::::NoneValue.into()), 191 | Some(old) => { 192 | // Increment the value read from storage. This will cause an error in the event 193 | // of overflow. 194 | let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; 195 | // Update the value in storage with the incremented result. 196 | Something::::put(new); 197 | Ok(()) 198 | }, 199 | } 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /pallets/template/src/mock.rs: -------------------------------------------------------------------------------- 1 | use crate as pallet_template; 2 | use frame_support::{ 3 | derive_impl, 4 | traits::{ConstU16, ConstU64}, 5 | }; 6 | use sp_core::H256; 7 | use sp_runtime::{ 8 | traits::{BlakeTwo256, IdentityLookup}, 9 | BuildStorage, 10 | }; 11 | 12 | type Block = frame_system::mocking::MockBlock; 13 | 14 | // Configure a mock runtime to test the pallet. 15 | frame_support::construct_runtime!( 16 | pub enum Test 17 | { 18 | System: frame_system, 19 | TemplateModule: pallet_template, 20 | } 21 | ); 22 | 23 | #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] 24 | impl frame_system::Config for Test { 25 | type BaseCallFilter = frame_support::traits::Everything; 26 | type BlockWeights = (); 27 | type BlockLength = (); 28 | type DbWeight = (); 29 | type RuntimeOrigin = RuntimeOrigin; 30 | type RuntimeCall = RuntimeCall; 31 | type Nonce = u64; 32 | type Hash = H256; 33 | type Hashing = BlakeTwo256; 34 | type AccountId = u64; 35 | type Lookup = IdentityLookup; 36 | type Block = Block; 37 | type RuntimeEvent = RuntimeEvent; 38 | type BlockHashCount = ConstU64<250>; 39 | type Version = (); 40 | type PalletInfo = PalletInfo; 41 | type AccountData = (); 42 | type OnNewAccount = (); 43 | type OnKilledAccount = (); 44 | type SystemWeightInfo = (); 45 | type SS58Prefix = ConstU16<42>; 46 | type OnSetCode = (); 47 | type MaxConsumers = frame_support::traits::ConstU32<16>; 48 | } 49 | 50 | impl pallet_template::Config for Test { 51 | type RuntimeEvent = RuntimeEvent; 52 | type WeightInfo = (); 53 | } 54 | 55 | // Build genesis storage according to the mock runtime. 56 | pub fn new_test_ext() -> sp_io::TestExternalities { 57 | frame_system::GenesisConfig::::default().build_storage().unwrap().into() 58 | } 59 | -------------------------------------------------------------------------------- /pallets/template/src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{mock::*, Error, Event, Something}; 2 | use frame_support::{assert_noop, assert_ok}; 3 | 4 | #[test] 5 | fn it_works_for_default_value() { 6 | new_test_ext().execute_with(|| { 7 | // Go past genesis block so events get deposited 8 | System::set_block_number(1); 9 | // Dispatch a signed extrinsic. 10 | assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); 11 | // Read pallet storage and assert an expected result. 12 | assert_eq!(Something::::get(), Some(42)); 13 | // Assert that the correct event was deposited 14 | System::assert_last_event(Event::SomethingStored { something: 42, who: 1 }.into()); 15 | }); 16 | } 17 | 18 | #[test] 19 | fn correct_error_for_none_value() { 20 | new_test_ext().execute_with(|| { 21 | // Ensure the expected error is thrown when no value is present. 22 | assert_noop!( 23 | TemplateModule::cause_error(RuntimeOrigin::signed(1)), 24 | Error::::NoneValue 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /pallets/template/src/weights.rs: -------------------------------------------------------------------------------- 1 | 2 | //! Autogenerated weights for pallet_template 3 | //! 4 | //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev 5 | //! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` 6 | //! WORST CASE MAP SIZE: `1000000` 7 | //! HOSTNAME: `Alexs-MacBook-Pro-2.local`, CPU: `` 8 | //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 9 | 10 | // Executed Command: 11 | // ../../target/release/node-template 12 | // benchmark 13 | // pallet 14 | // --chain 15 | // dev 16 | // --pallet 17 | // pallet_template 18 | // --extrinsic 19 | // * 20 | // --steps=50 21 | // --repeat=20 22 | // --wasm-execution=compiled 23 | // --output 24 | // pallets/template/src/weights.rs 25 | // --template 26 | // ../../.maintain/frame-weight-template.hbs 27 | 28 | #![cfg_attr(rustfmt, rustfmt_skip)] 29 | #![allow(unused_parens)] 30 | #![allow(unused_imports)] 31 | 32 | use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; 33 | use core::marker::PhantomData; 34 | 35 | /// Weight functions needed for pallet_template. 36 | pub trait WeightInfo { 37 | fn do_something() -> Weight; 38 | fn cause_error() -> Weight; 39 | } 40 | 41 | /// Weights for pallet_template using the Substrate node and recommended hardware. 42 | pub struct SubstrateWeight(PhantomData); 43 | impl WeightInfo for SubstrateWeight { 44 | /// Storage: TemplateModule Something (r:0 w:1) 45 | /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) 46 | fn do_something() -> Weight { 47 | // Proof Size summary in bytes: 48 | // Measured: `0` 49 | // Estimated: `0` 50 | // Minimum execution time: 8_000_000 picoseconds. 51 | Weight::from_parts(9_000_000, 0) 52 | .saturating_add(T::DbWeight::get().writes(1_u64)) 53 | } 54 | /// Storage: TemplateModule Something (r:1 w:1) 55 | /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) 56 | fn cause_error() -> Weight { 57 | // Proof Size summary in bytes: 58 | // Measured: `32` 59 | // Estimated: `1489` 60 | // Minimum execution time: 6_000_000 picoseconds. 61 | Weight::from_parts(6_000_000, 1489) 62 | .saturating_add(T::DbWeight::get().reads(1_u64)) 63 | .saturating_add(T::DbWeight::get().writes(1_u64)) 64 | } 65 | } 66 | 67 | // For backwards compatibility and tests 68 | impl WeightInfo for () { 69 | /// Storage: TemplateModule Something (r:0 w:1) 70 | /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) 71 | fn do_something() -> Weight { 72 | // Proof Size summary in bytes: 73 | // Measured: `0` 74 | // Estimated: `0` 75 | // Minimum execution time: 8_000_000 picoseconds. 76 | Weight::from_parts(9_000_000, 0) 77 | .saturating_add(RocksDbWeight::get().writes(1_u64)) 78 | } 79 | /// Storage: TemplateModule Something (r:1 w:1) 80 | /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) 81 | fn cause_error() -> Weight { 82 | // Proof Size summary in bytes: 83 | // Measured: `32` 84 | // Estimated: `1489` 85 | // Minimum execution time: 6_000_000 picoseconds. 86 | Weight::from_parts(6_000_000, 1489) 87 | .saturating_add(RocksDbWeight::get().reads(1_u64)) 88 | .saturating_add(RocksDbWeight::get().writes(1_u64)) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pallets/weight/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['Kaichao'] 3 | description = 'A pallet to demonstrate Substrate weight system' 4 | edition = '2021' 5 | homepage = 'https://substrate.dev' 6 | license = 'Unlicense' 7 | name = 'pallet-weight' 8 | repository = 'https://github.com/kaichaosun/play-substrate/' 9 | version = '4.0.0' 10 | 11 | [package.metadata.docs.rs] 12 | targets = ['x86_64-unknown-linux-gnu'] 13 | 14 | [dependencies.codec] 15 | default-features = false 16 | features = ['derive'] 17 | package = 'parity-scale-codec' 18 | version = '1.3.4' 19 | 20 | [dependencies] 21 | frame-support = { default-features = false, version = '2.0.0' } 22 | frame-system = { default-features = false, version = '2.0.0' } 23 | sp-runtime = { default-features = false, version = '2.0.0' } 24 | 25 | [dev-dependencies] 26 | sp-core = { default-features = false, version = '2.0.0' } 27 | sp-io = { default-features = false, version = '2.0.0' } 28 | 29 | [features] 30 | default = ['std'] 31 | std = [ 32 | 'codec/std', 33 | 'frame-support/std', 34 | 'frame-system/std', 35 | 'sp-runtime/std', 36 | ] 37 | -------------------------------------------------------------------------------- /pallets/weight/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | /// A FRAME pallet template with necessary imports 4 | 5 | /// Feel free to remove or edit this file as needed. 6 | /// If you change the name of this file, make sure to update its references in runtime/src/lib.rs 7 | /// If you remove this file, you can remove those references 8 | 9 | /// For more guidance on Substrate FRAME, see the example pallet 10 | /// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs 11 | 12 | use frame_support::{ 13 | decl_module, decl_storage, decl_event, decl_error, dispatch, 14 | weights::{ 15 | DispatchClass, 16 | ClassifyDispatch, 17 | WeighData, 18 | Weight, 19 | PaysFee, 20 | FunctionOf, 21 | Pays, 22 | } 23 | }; 24 | use frame_system::{self as system, ensure_signed}; 25 | use sp_runtime::traits::SaturatedConversion; 26 | 27 | struct WeightForCustomWeight(u32); 28 | 29 | impl WeighData<(&u32,)> for WeightForCustomWeight { 30 | fn weigh_data(&self, target: (&u32,)) -> Weight { 31 | let multiplier = self.0; 32 | (*target.0 * multiplier).saturated_into::() 33 | } 34 | } 35 | 36 | impl ClassifyDispatch<(&u32,)> for WeightForCustomWeight { 37 | fn classify_dispatch(&self, target: (&u32,)) -> DispatchClass { 38 | if *target.0 > 1000u32 { 39 | DispatchClass::Operational 40 | } else { 41 | DispatchClass::Normal 42 | } 43 | } 44 | } 45 | 46 | impl PaysFee<(&u32,)> for WeightForCustomWeight { 47 | fn pays_fee(&self, _target: (&u32,)) -> Pays { 48 | Pays::Yes 49 | } 50 | } 51 | 52 | /// The pallet's configuration trait. 53 | pub trait Trait: system::Trait { 54 | // Add other types and constants required to configure this pallet. 55 | 56 | /// The overarching event type. 57 | type Event: From> + Into<::Event>; 58 | } 59 | 60 | // This pallet's storage items. 61 | decl_storage! { 62 | // It is important to update your storage name so that your pallet's 63 | // storage items are isolated from other pallets. 64 | trait Store for Module as WeightModule { 65 | // Just a dummy storage item. 66 | // Here we are declaring a StorageValue, `Something` as a Option 67 | // `get(fn something)` is the default getter which returns either the stored `u32` or `None` if nothing stored 68 | Something get(fn something): Option; 69 | } 70 | } 71 | 72 | // The pallet's events 73 | decl_event!( 74 | pub enum Event where AccountId = ::AccountId { 75 | /// Just a dummy event. 76 | /// Event `Something` is declared with a parameter of the type `u32` and `AccountId` 77 | /// To emit this event, we call the deposit function, from our runtime functions 78 | SomethingStored(u32, AccountId), 79 | } 80 | ); 81 | 82 | // The pallet's errors 83 | decl_error! { 84 | pub enum Error for Module { 85 | /// Value was None 86 | NoneValue, 87 | /// Value reached maximum and cannot be incremented further 88 | StorageOverflow, 89 | } 90 | } 91 | 92 | // The pallet's dispatchable functions. 93 | decl_module! { 94 | /// The module declaration. 95 | pub struct Module for enum Call where origin: T::Origin { 96 | // Initializing errors 97 | // this includes information about your errors in the node's metadata. 98 | // it is needed only if you are using errors in your pallet 99 | type Error = Error; 100 | 101 | // Initializing events 102 | // this is needed only if you are using events in your pallet 103 | fn deposit_event() = default; 104 | 105 | /// Just a dummy entry point. 106 | /// function that can be called by the external world as an extrinsics call 107 | /// takes a parameter of the type `AccountId`, stores it, and emits an event 108 | /// 109 | /// # 110 | /// - `O(1)` 111 | /// - 1 DB change 112 | /// # 113 | #[weight = 100_000_000] 114 | pub fn fixed_weight_with_default(origin, something: u32) -> dispatch::DispatchResult { 115 | // Check it was signed and get the signer. See also: ensure_root and ensure_none 116 | let who = ensure_signed(origin)?; 117 | 118 | // Code to execute when something calls this. 119 | // For example: the following line stores the passed in u32 in the storage 120 | Something::put(something); 121 | 122 | // Here we are raising the Something event 123 | Self::deposit_event(RawEvent::SomethingStored(something, who)); 124 | Ok(()) 125 | } 126 | 127 | /// Just a dummy dispatchable call for operational purpose. 128 | #[weight = (100_000_000, DispatchClass::Operational)] 129 | pub fn fixed_weight_with_operational(origin, something: u32) -> dispatch::DispatchResult { 130 | // Check it was signed and get the signer. See also: ensure_root and ensure_none 131 | let who = ensure_signed(origin)?; 132 | 133 | // Code to execute when something calls this. 134 | // For example: the following line stores the passed in u32 in the storage 135 | Something::put(something); 136 | 137 | // Here we are raising the Something event 138 | Self::deposit_event(RawEvent::SomethingStored(something, who)); 139 | Ok(()) 140 | } 141 | 142 | /// Just a dummy dispatchable call for custom weight. 143 | #[weight = WeightForCustomWeight(100u32)] 144 | pub fn custom_weight(origin, something: u32) -> dispatch::DispatchResult { 145 | // Check it was signed and get the signer. See also: ensure_root and ensure_none 146 | let who = ensure_signed(origin)?; 147 | 148 | // Code to execute when something calls this. 149 | // For example: the following line stores the passed in u32 in the storage 150 | Something::put(something); 151 | 152 | // Here we are raising the Something event 153 | Self::deposit_event(RawEvent::SomethingStored(something, who)); 154 | Ok(()) 155 | } 156 | 157 | /// FunctionOf is deprecated and not suggest to use any more. 158 | /// Just a dummy dispatchable call for FunctionOf weight. 159 | #[weight = FunctionOf(|args: (&u32,)| (args.0 * 10) as Weight, DispatchClass::Normal, Pays::Yes)] 160 | pub fn function_of_weight(origin, something: u32) -> dispatch::DispatchResult { 161 | // Check it was signed and get the signer. See also: ensure_root and ensure_none 162 | let who = ensure_signed(origin)?; 163 | 164 | // Code to execute when something calls this. 165 | // For example: the following line stores the passed in u32 in the storage 166 | Something::put(something); 167 | 168 | // Here we are raising the Something event 169 | Self::deposit_event(RawEvent::SomethingStored(something, who)); 170 | Ok(()) 171 | } 172 | 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "node-template-runtime" 3 | version = "4.0.0-dev" 4 | description = "A fresh FRAME-based Substrate node, ready for hacking." 5 | authors = ["Substrate DevHub "] 6 | homepage = "https://substrate.io/" 7 | edition = "2021" 8 | license = "Unlicense" 9 | publish = false 10 | repository = "https://github.com/substrate-developer-hub/substrate-node-template/" 11 | 12 | [package.metadata.docs.rs] 13 | targets = ["x86_64-unknown-linux-gnu"] 14 | 15 | [dependencies] 16 | codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } 17 | scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } 18 | serde_json = { version = "1.0.114", default-features = false, features = ["alloc"] } 19 | 20 | pallet-aura = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 21 | pallet-balances = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 22 | frame-support = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 23 | pallet-grandpa = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 24 | pallet-sudo = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 25 | frame-system = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 26 | frame-try-runtime = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", optional = true, tag = "polkadot-v1.8.0" } 27 | pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 28 | pallet-transaction-payment = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 29 | frame-executive = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 30 | sp-api = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 31 | sp-block-builder = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 32 | sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 33 | sp-consensus-grandpa = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 34 | sp-core = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 35 | sp-inherents = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 36 | sp-offchain = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 37 | sp-runtime = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 38 | sp-session = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 39 | sp-std = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 40 | sp-transaction-pool = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 41 | sp-version = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 42 | sp-genesis-builder = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 43 | sp-storage = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 44 | 45 | # Used for the node template's RPCs 46 | frame-system-rpc-runtime-api = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 47 | pallet-transaction-payment-rpc-runtime-api = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-v1.8.0" } 48 | 49 | # Used for runtime benchmarking 50 | frame-benchmarking = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", optional = true, tag = "polkadot-v1.8.0" } 51 | frame-system-benchmarking = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", optional = true, tag = "polkadot-v1.8.0" } 52 | 53 | # Local Dependencies 54 | pallet-template = { default-features = false, path = "../pallets/template" } 55 | pallet-poe = { default-features = false, path = "../pallets/poe" } 56 | pallet-data-type = { default-features = false, path = "../pallets/data-type" } 57 | 58 | [build-dependencies] 59 | substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk.git", optional = true, tag = "polkadot-v1.8.0" } 60 | 61 | [features] 62 | default = ["std"] 63 | std = [ 64 | "codec/std", 65 | "frame-benchmarking?/std", 66 | "frame-executive/std", 67 | "frame-support/std", 68 | "frame-system-benchmarking?/std", 69 | "frame-system-rpc-runtime-api/std", 70 | "frame-system/std", 71 | "frame-try-runtime?/std", 72 | "pallet-aura/std", 73 | "pallet-balances/std", 74 | "pallet-grandpa/std", 75 | "pallet-sudo/std", 76 | "pallet-template/std", 77 | "pallet-timestamp/std", 78 | "pallet-transaction-payment-rpc-runtime-api/std", 79 | "pallet-transaction-payment/std", 80 | "scale-info/std", 81 | "serde_json/std", 82 | "sp-api/std", 83 | "sp-block-builder/std", 84 | "sp-consensus-aura/std", 85 | "sp-consensus-grandpa/std", 86 | "sp-core/std", 87 | "sp-genesis-builder/std", 88 | "sp-inherents/std", 89 | "sp-offchain/std", 90 | "sp-runtime/std", 91 | "sp-session/std", 92 | "sp-std/std", 93 | "sp-storage/std", 94 | "sp-transaction-pool/std", 95 | "sp-version/std", 96 | "substrate-wasm-builder", 97 | ] 98 | runtime-benchmarks = [ 99 | "frame-benchmarking/runtime-benchmarks", 100 | "frame-support/runtime-benchmarks", 101 | "frame-system-benchmarking/runtime-benchmarks", 102 | "frame-system/runtime-benchmarks", 103 | "pallet-balances/runtime-benchmarks", 104 | "pallet-grandpa/runtime-benchmarks", 105 | "pallet-sudo/runtime-benchmarks", 106 | "pallet-template/runtime-benchmarks", 107 | "pallet-timestamp/runtime-benchmarks", 108 | "sp-runtime/runtime-benchmarks", 109 | ] 110 | try-runtime = [ 111 | "frame-executive/try-runtime", 112 | "frame-support/try-runtime", 113 | "frame-system/try-runtime", 114 | "frame-try-runtime/try-runtime", 115 | "pallet-aura/try-runtime", 116 | "pallet-balances/try-runtime", 117 | "pallet-grandpa/try-runtime", 118 | "pallet-sudo/try-runtime", 119 | "pallet-template/try-runtime", 120 | "pallet-timestamp/try-runtime", 121 | "pallet-transaction-payment/try-runtime", 122 | "sp-runtime/try-runtime", 123 | ] 124 | experimental = ["pallet-aura/experimental"] 125 | -------------------------------------------------------------------------------- /runtime/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | #[cfg(feature = "std")] 3 | { 4 | substrate_wasm_builder::WasmBuilder::new() 5 | .with_current_project() 6 | .export_heap_base() 7 | .import_memory() 8 | .build(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. 3 | #![recursion_limit = "256"] 4 | 5 | // Make the WASM binary available. 6 | #[cfg(feature = "std")] 7 | include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); 8 | 9 | use pallet_grandpa::AuthorityId as GrandpaId; 10 | use sp_api::impl_runtime_apis; 11 | use sp_consensus_aura::sr25519::AuthorityId as AuraId; 12 | use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; 13 | use sp_runtime::{ 14 | create_runtime_str, generic, impl_opaque_keys, 15 | traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, NumberFor, One, Verify}, 16 | transaction_validity::{TransactionSource, TransactionValidity}, 17 | ApplyExtrinsicResult, MultiSignature, 18 | }; 19 | use sp_std::prelude::*; 20 | #[cfg(feature = "std")] 21 | use sp_version::NativeVersion; 22 | use sp_version::RuntimeVersion; 23 | 24 | use frame_support::genesis_builder_helper::{build_config, create_default_config}; 25 | // A few exports that help ease life for downstream crates. 26 | pub use frame_support::{ 27 | construct_runtime, derive_impl, parameter_types, 28 | traits::{ 29 | ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, KeyOwnerProofSystem, Randomness, 30 | StorageInfo, 31 | }, 32 | weights::{ 33 | constants::{ 34 | BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, 35 | }, 36 | IdentityFee, Weight, 37 | }, 38 | StorageValue, 39 | }; 40 | pub use frame_system::Call as SystemCall; 41 | pub use pallet_balances::Call as BalancesCall; 42 | pub use pallet_timestamp::Call as TimestampCall; 43 | use pallet_transaction_payment::{ConstFeeMultiplier, CurrencyAdapter, Multiplier}; 44 | #[cfg(any(feature = "std", test))] 45 | pub use sp_runtime::BuildStorage; 46 | pub use sp_runtime::{Perbill, Permill}; 47 | 48 | /// Import the template pallet. 49 | pub use pallet_template; 50 | 51 | /// An index to a block. 52 | pub type BlockNumber = u32; 53 | 54 | /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. 55 | pub type Signature = MultiSignature; 56 | 57 | /// Some way of identifying an account on the chain. We intentionally make it equivalent 58 | /// to the public key of our transaction signing scheme. 59 | pub type AccountId = <::Signer as IdentifyAccount>::AccountId; 60 | 61 | /// Balance of an account. 62 | pub type Balance = u128; 63 | 64 | /// Index of a transaction in the chain. 65 | pub type Nonce = u32; 66 | 67 | /// A hash of some data used by the chain. 68 | pub type Hash = sp_core::H256; 69 | 70 | /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know 71 | /// the specifics of the runtime. They can then be made to be agnostic over specific formats 72 | /// of data like extrinsics, allowing for them to continue syncing the network through upgrades 73 | /// to even the core data structures. 74 | pub mod opaque { 75 | use super::*; 76 | 77 | pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; 78 | 79 | /// Opaque block header type. 80 | pub type Header = generic::Header; 81 | /// Opaque block type. 82 | pub type Block = generic::Block; 83 | /// Opaque block identifier type. 84 | pub type BlockId = generic::BlockId; 85 | 86 | impl_opaque_keys! { 87 | pub struct SessionKeys { 88 | pub aura: Aura, 89 | pub grandpa: Grandpa, 90 | } 91 | } 92 | } 93 | 94 | // To learn more about runtime versioning, see: 95 | // https://docs.substrate.io/main-docs/build/upgrade#runtime-versioning 96 | #[sp_version::runtime_version] 97 | pub const VERSION: RuntimeVersion = RuntimeVersion { 98 | spec_name: create_runtime_str!("node-template"), 99 | impl_name: create_runtime_str!("node-template"), 100 | authoring_version: 1, 101 | // The version of the runtime specification. A full node will not attempt to use its native 102 | // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, 103 | // `spec_version`, and `authoring_version` are the same between Wasm and native. 104 | // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use 105 | // the compatible custom types. 106 | spec_version: 100, 107 | impl_version: 1, 108 | apis: RUNTIME_API_VERSIONS, 109 | transaction_version: 1, 110 | state_version: 1, 111 | }; 112 | 113 | /// This determines the average expected block time that we are targeting. 114 | /// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. 115 | /// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked 116 | /// up by `pallet_aura` to implement `fn slot_duration()`. 117 | /// 118 | /// Change this to adjust the block time. 119 | pub const MILLISECS_PER_BLOCK: u64 = 6000; 120 | 121 | // NOTE: Currently it is not possible to change the slot duration after the chain has started. 122 | // Attempting to do so will brick block production. 123 | pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; 124 | 125 | // Time is measured by number of blocks. 126 | pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); 127 | pub const HOURS: BlockNumber = MINUTES * 60; 128 | pub const DAYS: BlockNumber = HOURS * 24; 129 | 130 | /// The version information used to identify this runtime when compiled natively. 131 | #[cfg(feature = "std")] 132 | pub fn native_version() -> NativeVersion { 133 | NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } 134 | } 135 | 136 | const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); 137 | 138 | parameter_types! { 139 | pub const BlockHashCount: BlockNumber = 2400; 140 | pub const Version: RuntimeVersion = VERSION; 141 | /// We allow for 2 seconds of compute with a 6 second average block time. 142 | pub BlockWeights: frame_system::limits::BlockWeights = 143 | frame_system::limits::BlockWeights::with_sensible_defaults( 144 | Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), 145 | NORMAL_DISPATCH_RATIO, 146 | ); 147 | pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength 148 | ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); 149 | pub const SS58Prefix: u8 = 42; 150 | } 151 | 152 | /// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from 153 | /// [`SoloChainDefaultConfig`](`struct@frame_system::config_preludes::SolochainDefaultConfig`), 154 | /// but overridden as needed. 155 | #[derive_impl(frame_system::config_preludes::SolochainDefaultConfig as frame_system::DefaultConfig)] 156 | impl frame_system::Config for Runtime { 157 | /// The block type for the runtime. 158 | type Block = Block; 159 | /// Block & extrinsics weights: base values and limits. 160 | type BlockWeights = BlockWeights; 161 | /// The maximum length of a block (in bytes). 162 | type BlockLength = BlockLength; 163 | /// The identifier used to distinguish between accounts. 164 | type AccountId = AccountId; 165 | /// The type for storing how many extrinsics an account has signed. 166 | type Nonce = Nonce; 167 | /// The type for hashing blocks and tries. 168 | type Hash = Hash; 169 | /// Maximum number of block number to block hash mappings to keep (oldest pruned first). 170 | type BlockHashCount = BlockHashCount; 171 | /// The weight of database operations that the runtime can invoke. 172 | type DbWeight = RocksDbWeight; 173 | /// Version of the runtime. 174 | type Version = Version; 175 | /// The data to be stored in an account. 176 | type AccountData = pallet_balances::AccountData; 177 | /// This is used as an identifier of the chain. 42 is the generic substrate prefix. 178 | type SS58Prefix = SS58Prefix; 179 | type MaxConsumers = frame_support::traits::ConstU32<16>; 180 | } 181 | 182 | impl pallet_aura::Config for Runtime { 183 | type AuthorityId = AuraId; 184 | type DisabledValidators = (); 185 | type MaxAuthorities = ConstU32<32>; 186 | type AllowMultipleBlocksPerSlot = ConstBool; 187 | 188 | #[cfg(feature = "experimental")] 189 | type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; 190 | } 191 | 192 | impl pallet_grandpa::Config for Runtime { 193 | type RuntimeEvent = RuntimeEvent; 194 | 195 | type WeightInfo = (); 196 | type MaxAuthorities = ConstU32<32>; 197 | type MaxNominators = ConstU32<0>; 198 | type MaxSetIdSessionEntries = ConstU64<0>; 199 | 200 | type KeyOwnerProof = sp_core::Void; 201 | type EquivocationReportSystem = (); 202 | } 203 | 204 | impl pallet_timestamp::Config for Runtime { 205 | /// A timestamp: milliseconds since the unix epoch. 206 | type Moment = u64; 207 | type OnTimestampSet = Aura; 208 | type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; 209 | type WeightInfo = (); 210 | } 211 | 212 | /// Existential deposit. 213 | pub const EXISTENTIAL_DEPOSIT: u128 = 500; 214 | 215 | impl pallet_balances::Config for Runtime { 216 | type MaxLocks = ConstU32<50>; 217 | type MaxReserves = (); 218 | type ReserveIdentifier = [u8; 8]; 219 | /// The type for recording an account's balance. 220 | type Balance = Balance; 221 | /// The ubiquitous event type. 222 | type RuntimeEvent = RuntimeEvent; 223 | type DustRemoval = (); 224 | type ExistentialDeposit = ConstU128; 225 | type AccountStore = System; 226 | type WeightInfo = pallet_balances::weights::SubstrateWeight; 227 | type FreezeIdentifier = (); 228 | type MaxFreezes = (); 229 | type RuntimeHoldReason = (); 230 | type RuntimeFreezeReason = (); 231 | } 232 | 233 | parameter_types! { 234 | pub FeeMultiplier: Multiplier = Multiplier::one(); 235 | } 236 | 237 | impl pallet_transaction_payment::Config for Runtime { 238 | type RuntimeEvent = RuntimeEvent; 239 | type OnChargeTransaction = CurrencyAdapter; 240 | type OperationalFeeMultiplier = ConstU8<5>; 241 | type WeightToFee = IdentityFee; 242 | type LengthToFee = IdentityFee; 243 | type FeeMultiplierUpdate = ConstFeeMultiplier; 244 | } 245 | 246 | impl pallet_sudo::Config for Runtime { 247 | type RuntimeEvent = RuntimeEvent; 248 | type RuntimeCall = RuntimeCall; 249 | type WeightInfo = pallet_sudo::weights::SubstrateWeight; 250 | } 251 | 252 | /// Configure the pallet-template in pallets/template. 253 | impl pallet_template::Config for Runtime { 254 | type RuntimeEvent = RuntimeEvent; 255 | type WeightInfo = pallet_template::weights::SubstrateWeight; 256 | } 257 | 258 | // Create the runtime by composing the FRAME pallets that were previously configured. 259 | construct_runtime!( 260 | pub enum Runtime { 261 | System: frame_system, 262 | Timestamp: pallet_timestamp, 263 | Aura: pallet_aura, 264 | Grandpa: pallet_grandpa, 265 | Balances: pallet_balances, 266 | TransactionPayment: pallet_transaction_payment, 267 | Sudo: pallet_sudo, 268 | // Include the custom logic from the pallet-template in the runtime. 269 | TemplateModule: pallet_template, 270 | } 271 | ); 272 | 273 | /// The address format for describing accounts. 274 | pub type Address = sp_runtime::MultiAddress; 275 | /// Block header type as expected by this runtime. 276 | pub type Header = generic::Header; 277 | /// Block type as expected by this runtime. 278 | pub type Block = generic::Block; 279 | /// The SignedExtension to the basic transaction logic. 280 | pub type SignedExtra = ( 281 | frame_system::CheckNonZeroSender, 282 | frame_system::CheckSpecVersion, 283 | frame_system::CheckTxVersion, 284 | frame_system::CheckGenesis, 285 | frame_system::CheckEra, 286 | frame_system::CheckNonce, 287 | frame_system::CheckWeight, 288 | pallet_transaction_payment::ChargeTransactionPayment, 289 | ); 290 | 291 | /// All migrations of the runtime, aside from the ones declared in the pallets. 292 | /// 293 | /// This can be a tuple of types, each implementing `OnRuntimeUpgrade`. 294 | #[allow(unused_parens)] 295 | type Migrations = (); 296 | 297 | /// Unchecked extrinsic type as expected by this runtime. 298 | pub type UncheckedExtrinsic = 299 | generic::UncheckedExtrinsic; 300 | /// The payload being signed in transactions. 301 | pub type SignedPayload = generic::SignedPayload; 302 | /// Executive: handles dispatch to the various modules. 303 | pub type Executive = frame_executive::Executive< 304 | Runtime, 305 | Block, 306 | frame_system::ChainContext, 307 | Runtime, 308 | AllPalletsWithSystem, 309 | Migrations, 310 | >; 311 | 312 | #[cfg(feature = "runtime-benchmarks")] 313 | mod benches { 314 | frame_benchmarking::define_benchmarks!( 315 | [frame_benchmarking, BaselineBench::] 316 | [frame_system, SystemBench::] 317 | [pallet_balances, Balances] 318 | [pallet_timestamp, Timestamp] 319 | [pallet_sudo, Sudo] 320 | [pallet_template, TemplateModule] 321 | ); 322 | } 323 | 324 | impl_runtime_apis! { 325 | impl sp_api::Core for Runtime { 326 | fn version() -> RuntimeVersion { 327 | VERSION 328 | } 329 | 330 | fn execute_block(block: Block) { 331 | Executive::execute_block(block); 332 | } 333 | 334 | fn initialize_block(header: &::Header) { 335 | Executive::initialize_block(header) 336 | } 337 | } 338 | 339 | impl sp_api::Metadata for Runtime { 340 | fn metadata() -> OpaqueMetadata { 341 | OpaqueMetadata::new(Runtime::metadata().into()) 342 | } 343 | 344 | fn metadata_at_version(version: u32) -> Option { 345 | Runtime::metadata_at_version(version) 346 | } 347 | 348 | fn metadata_versions() -> sp_std::vec::Vec { 349 | Runtime::metadata_versions() 350 | } 351 | } 352 | 353 | impl sp_block_builder::BlockBuilder for Runtime { 354 | fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { 355 | Executive::apply_extrinsic(extrinsic) 356 | } 357 | 358 | fn finalize_block() -> ::Header { 359 | Executive::finalize_block() 360 | } 361 | 362 | fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { 363 | data.create_extrinsics() 364 | } 365 | 366 | fn check_inherents( 367 | block: Block, 368 | data: sp_inherents::InherentData, 369 | ) -> sp_inherents::CheckInherentsResult { 370 | data.check_extrinsics(&block) 371 | } 372 | } 373 | 374 | impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { 375 | fn validate_transaction( 376 | source: TransactionSource, 377 | tx: ::Extrinsic, 378 | block_hash: ::Hash, 379 | ) -> TransactionValidity { 380 | Executive::validate_transaction(source, tx, block_hash) 381 | } 382 | } 383 | 384 | impl sp_offchain::OffchainWorkerApi for Runtime { 385 | fn offchain_worker(header: &::Header) { 386 | Executive::offchain_worker(header) 387 | } 388 | } 389 | 390 | impl sp_consensus_aura::AuraApi for Runtime { 391 | fn slot_duration() -> sp_consensus_aura::SlotDuration { 392 | sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) 393 | } 394 | 395 | fn authorities() -> Vec { 396 | Aura::authorities().into_inner() 397 | } 398 | } 399 | 400 | impl sp_session::SessionKeys for Runtime { 401 | fn generate_session_keys(seed: Option>) -> Vec { 402 | opaque::SessionKeys::generate(seed) 403 | } 404 | 405 | fn decode_session_keys( 406 | encoded: Vec, 407 | ) -> Option, KeyTypeId)>> { 408 | opaque::SessionKeys::decode_into_raw_public_keys(&encoded) 409 | } 410 | } 411 | 412 | impl sp_consensus_grandpa::GrandpaApi for Runtime { 413 | fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { 414 | Grandpa::grandpa_authorities() 415 | } 416 | 417 | fn current_set_id() -> sp_consensus_grandpa::SetId { 418 | Grandpa::current_set_id() 419 | } 420 | 421 | fn submit_report_equivocation_unsigned_extrinsic( 422 | _equivocation_proof: sp_consensus_grandpa::EquivocationProof< 423 | ::Hash, 424 | NumberFor, 425 | >, 426 | _key_owner_proof: sp_consensus_grandpa::OpaqueKeyOwnershipProof, 427 | ) -> Option<()> { 428 | None 429 | } 430 | 431 | fn generate_key_ownership_proof( 432 | _set_id: sp_consensus_grandpa::SetId, 433 | _authority_id: GrandpaId, 434 | ) -> Option { 435 | // NOTE: this is the only implementation possible since we've 436 | // defined our key owner proof type as a bottom type (i.e. a type 437 | // with no values). 438 | None 439 | } 440 | } 441 | 442 | impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { 443 | fn account_nonce(account: AccountId) -> Nonce { 444 | System::account_nonce(account) 445 | } 446 | } 447 | 448 | impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { 449 | fn query_info( 450 | uxt: ::Extrinsic, 451 | len: u32, 452 | ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { 453 | TransactionPayment::query_info(uxt, len) 454 | } 455 | fn query_fee_details( 456 | uxt: ::Extrinsic, 457 | len: u32, 458 | ) -> pallet_transaction_payment::FeeDetails { 459 | TransactionPayment::query_fee_details(uxt, len) 460 | } 461 | fn query_weight_to_fee(weight: Weight) -> Balance { 462 | TransactionPayment::weight_to_fee(weight) 463 | } 464 | fn query_length_to_fee(length: u32) -> Balance { 465 | TransactionPayment::length_to_fee(length) 466 | } 467 | } 468 | 469 | impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi 470 | for Runtime 471 | { 472 | fn query_call_info( 473 | call: RuntimeCall, 474 | len: u32, 475 | ) -> pallet_transaction_payment::RuntimeDispatchInfo { 476 | TransactionPayment::query_call_info(call, len) 477 | } 478 | fn query_call_fee_details( 479 | call: RuntimeCall, 480 | len: u32, 481 | ) -> pallet_transaction_payment::FeeDetails { 482 | TransactionPayment::query_call_fee_details(call, len) 483 | } 484 | fn query_weight_to_fee(weight: Weight) -> Balance { 485 | TransactionPayment::weight_to_fee(weight) 486 | } 487 | fn query_length_to_fee(length: u32) -> Balance { 488 | TransactionPayment::length_to_fee(length) 489 | } 490 | } 491 | 492 | #[cfg(feature = "runtime-benchmarks")] 493 | impl frame_benchmarking::Benchmark for Runtime { 494 | fn benchmark_metadata(extra: bool) -> ( 495 | Vec, 496 | Vec, 497 | ) { 498 | use frame_benchmarking::{baseline, Benchmarking, BenchmarkList}; 499 | use frame_support::traits::StorageInfoTrait; 500 | use frame_system_benchmarking::Pallet as SystemBench; 501 | use baseline::Pallet as BaselineBench; 502 | 503 | let mut list = Vec::::new(); 504 | list_benchmarks!(list, extra); 505 | 506 | let storage_info = AllPalletsWithSystem::storage_info(); 507 | 508 | (list, storage_info) 509 | } 510 | 511 | fn dispatch_benchmark( 512 | config: frame_benchmarking::BenchmarkConfig 513 | ) -> Result, sp_runtime::RuntimeString> { 514 | use frame_benchmarking::{baseline, Benchmarking, BenchmarkBatch}; 515 | use sp_storage::TrackedStorageKey; 516 | use frame_system_benchmarking::Pallet as SystemBench; 517 | use baseline::Pallet as BaselineBench; 518 | 519 | impl frame_system_benchmarking::Config for Runtime {} 520 | impl baseline::Config for Runtime {} 521 | 522 | use frame_support::traits::WhitelistedStorageKeys; 523 | let whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); 524 | 525 | let mut batches = Vec::::new(); 526 | let params = (&config, &whitelist); 527 | add_benchmarks!(params, batches); 528 | 529 | Ok(batches) 530 | } 531 | } 532 | 533 | #[cfg(feature = "try-runtime")] 534 | impl frame_try_runtime::TryRuntime for Runtime { 535 | fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { 536 | // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to 537 | // have a backtrace here. If any of the pre/post migration checks fail, we shall stop 538 | // right here and right now. 539 | let weight = Executive::try_runtime_upgrade(checks).unwrap(); 540 | (weight, BlockWeights::get().max_block) 541 | } 542 | 543 | fn execute_block( 544 | block: Block, 545 | state_root_check: bool, 546 | signature_check: bool, 547 | select: frame_try_runtime::TryStateSelect 548 | ) -> Weight { 549 | // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to 550 | // have a backtrace here. 551 | Executive::try_execute_block(block, state_root_check, signature_check, select).expect("execute-block failed") 552 | } 553 | } 554 | 555 | impl sp_genesis_builder::GenesisBuilder for Runtime { 556 | fn create_default_config() -> Vec { 557 | create_default_config::() 558 | } 559 | 560 | fn build_config(config: Vec) -> sp_genesis_builder::Result { 561 | build_config::(config) 562 | } 563 | } 564 | } 565 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | components = [ 4 | "cargo", 5 | "clippy", 6 | "rust-analyzer", 7 | "rust-src", 8 | "rust-std", 9 | "rustc-dev", 10 | "rustc", 11 | "rustfmt", 12 | ] 13 | targets = [ "wasm32-unknown-unknown" ] 14 | profile = "minimal" 15 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Basic 2 | hard_tabs = true 3 | max_width = 100 4 | use_small_heuristics = "Max" 5 | # Imports 6 | imports_granularity = "Crate" 7 | reorder_imports = true 8 | # Consistency 9 | newline_style = "Unix" 10 | # Format comments 11 | comment_width = 100 12 | wrap_comments = true 13 | # Misc 14 | chain_width = 80 15 | spaces_around_ranges = false 16 | binop_separator = "Back" 17 | reorder_impl_items = false 18 | match_arm_leading_pipes = "Preserve" 19 | match_arm_blocks = false 20 | match_block_trailing_comma = true 21 | trailing_comma = "Vertical" 22 | trailing_semicolon = false 23 | use_field_init_shorthand = true 24 | -------------------------------------------------------------------------------- /scripts/docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This script is meant to be run on Unix/Linux based systems 3 | set -e 4 | 5 | echo "*** Start Substrate node template ***" 6 | 7 | cd $(dirname ${BASH_SOURCE[0]})/.. 8 | 9 | docker-compose down --remove-orphans 10 | docker-compose run --rm --service-ports dev $@ 11 | -------------------------------------------------------------------------------- /scripts/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This script is meant to be run on Unix/Linux based systems 3 | set -e 4 | 5 | echo "*** Initializing WASM build environment" 6 | 7 | if [ -z $CI_PROJECT_NAME ] ; then 8 | rustup update nightly 9 | rustup update stable 10 | fi 11 | 12 | rustup target add wasm32-unknown-unknown --toolchain nightly 13 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | mozillaOverlay = 3 | import (builtins.fetchGit { 4 | url = "https://github.com/mozilla/nixpkgs-mozilla.git"; 5 | rev = "57c8084c7ef41366993909c20491e359bbb90f54"; 6 | }); 7 | pinned = builtins.fetchGit { 8 | # Descriptive name to make the store path easier to identify 9 | url = "https://github.com/nixos/nixpkgs/"; 10 | # Commit hash for nixos-unstable as of 2020-04-26 11 | # `git ls-remote https://github.com/nixos/nixpkgs nixos-unstable` 12 | ref = "refs/heads/nixos-unstable"; 13 | rev = "1fe6ed37fd9beb92afe90671c0c2a662a03463dd"; 14 | }; 15 | nixpkgs = import pinned { overlays = [ mozillaOverlay ]; }; 16 | toolchain = with nixpkgs; (rustChannelOf { date = "2021-09-14"; channel = "nightly"; }); 17 | rust-wasm = toolchain.rust.override { 18 | targets = [ "wasm32-unknown-unknown" ]; 19 | }; 20 | in 21 | with nixpkgs; pkgs.mkShell { 22 | buildInputs = [ 23 | clang 24 | pkg-config 25 | rust-wasm 26 | ] ++ stdenv.lib.optionals stdenv.isDarwin [ 27 | darwin.apple_sdk.frameworks.Security 28 | ]; 29 | 30 | LIBCLANG_PATH = "${llvmPackages.libclang}/lib"; 31 | PROTOC = "${protobuf}/bin/protoc"; 32 | RUST_SRC_PATH = "${toolchain.rust-src}/lib/rustlib/src/rust/library/"; 33 | ROCKSDB_LIB_DIR = "${rocksdb}/lib"; 34 | 35 | } 36 | --------------------------------------------------------------------------------