├── Gemfile ├── LICENSE ├── README.md ├── _config.yml ├── node ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── metalog.json ├── runtime │ ├── Cargo.lock │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ └── template.rs │ └── wasm │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── build.sh │ │ └── src │ │ └── lib.rs ├── scripts │ ├── build.sh │ └── init.sh └── src │ ├── chain_spec.rs │ ├── cli.rs │ ├── error.rs │ ├── main.rs │ └── service.rs ├── tutorial ├── 1_introduction.md ├── 2_setup.md ├── 3_interface.md ├── 4_runtime.md ├── 5_testing.md ├── 6_unit_tests.md ├── 7_ui.md ├── 8_next_steps.md └── images │ ├── interface_1.png │ ├── interface_2.png │ ├── interface_3.png │ ├── setup_1.png │ ├── testing_1.png │ ├── testing_2.png │ ├── testing_3.png │ ├── testing_4.png │ ├── tests_1.png │ ├── ui_1.png │ └── ui_2.png └── ui ├── .gitignore ├── css └── style.css ├── dist ├── index.html └── main.js ├── package-lock.json ├── package.json ├── src └── index.js └── webpack.config.js /Gemfile: -------------------------------------------------------------------------------- 1 | gem "minima" 2 | gem 'jekyll-titles-from-headings' 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 PACT Care BV 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Substrate Web3 DNS Tutorial 2 | 3 | This repository contains a [Substrate](https://github.com/paritytech/substrate) tutorial for a simple Substrate Web3 Domain Name System. It’s based on the [Stars Network](https://github.com/PACTCare/Stars-Network/blob/master/WHITEPAPER.md) and specifically the [Starlog runtime](https://github.com/PACTCare/Starlog). 4 | 5 | ## Table of Contents 6 | 7 | 1. [Introduction](./tutorial/1_introduction.md) 8 | 2. [Setup](./tutorial/2_setup.md) 9 | 3. [Interface](./tutorial/3_interface.md) 10 | 4. [Runtime](./tutorial/4_runtime.md) 11 | 5. [Testing](./tutorial/5_testing.md) 12 | 6. [Unit Tests](./tutorial/6_unit_tests.md) 13 | 7. [UI](./tutorial/7_ui.md) 14 | 8. [Next Steps](./tutorial/8_next_steps.md) 15 | 16 | ## Maintainer 17 | 18 | [David Hawig](https://github.com/Noc2) 19 | 20 | ## Contributing 21 | 22 | If you want to help either join our **[discord server](https://discord.gg/VMj7PFN)** or you can open issues. You can also submit pull requests to this repository. 23 | 24 | ## Start Tutorial 25 | 26 | **-> [Continue on GitHub](./tutorial/1_introduction.md)** 27 | 28 | **-> [Open Tutorial Website](https://pactcare.github.io/Substrate-Web3-DNS-Tutorial/tutorial/1_introduction.html)** 29 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: null 2 | name: Substrate Web3 DNS Tutorial 3 | 4 | plugins: 5 | - jekyll-titles-from-headings 6 | 7 | exclude: ["README.md"] 8 | -------------------------------------------------------------------------------- /node/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | 5 | # These are backup files generated by rustfmt 6 | **/*.rs.bk 7 | -------------------------------------------------------------------------------- /node/Cargo.toml: -------------------------------------------------------------------------------- 1 | [dependencies] 2 | error-chain = '0.12' 3 | exit-future = '0.1' 4 | futures = '0.1' 5 | hex-literal = '0.1' 6 | log = '0.4' 7 | parity-codec = '3.2' 8 | parking_lot = '0.7.1' 9 | tokio = '0.1' 10 | trie-root = '0.12.0' 11 | 12 | [dependencies.basic-authorship] 13 | git = 'https://github.com/paritytech/substrate.git' 14 | package = 'substrate-basic-authorship' 15 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 16 | 17 | [dependencies.consensus] 18 | git = 'https://github.com/paritytech/substrate.git' 19 | package = 'substrate-consensus-aura' 20 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 21 | 22 | [dependencies.ctrlc] 23 | features = ['termination'] 24 | version = '3.0' 25 | 26 | [dependencies.inherents] 27 | git = 'https://github.com/paritytech/substrate.git' 28 | package = 'substrate-inherents' 29 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 30 | 31 | [dependencies.network] 32 | git = 'https://github.com/paritytech/substrate.git' 33 | package = 'substrate-network' 34 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 35 | 36 | [dependencies.web3-dns-tutorial-runtime] 37 | path = 'runtime' 38 | 39 | [dependencies.primitives] 40 | git = 'https://github.com/paritytech/substrate.git' 41 | package = 'substrate-primitives' 42 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 43 | 44 | [dependencies.sr-io] 45 | git = 'https://github.com/paritytech/substrate.git' 46 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 47 | 48 | [dependencies.substrate-cli] 49 | git = 'https://github.com/paritytech/substrate.git' 50 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 51 | 52 | [dependencies.substrate-client] 53 | git = 'https://github.com/paritytech/substrate.git' 54 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 55 | 56 | [dependencies.substrate-executor] 57 | git = 'https://github.com/paritytech/substrate.git' 58 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 59 | 60 | [dependencies.substrate-service] 61 | git = 'https://github.com/paritytech/substrate.git' 62 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 63 | 64 | [dependencies.transaction-pool] 65 | git = 'https://github.com/paritytech/substrate.git' 66 | package = 'substrate-transaction-pool' 67 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 68 | 69 | [build-dependencies] 70 | vergen = '3' 71 | 72 | [[bin]] 73 | name = 'web3-dns-tutorial' 74 | path = 'src/main.rs' 75 | 76 | [package] 77 | authors = ['Parity Technologies '] 78 | build = 'build.rs' 79 | edition = '2018' 80 | name = 'web3-dns-tutorial' 81 | version = '1.0.0' 82 | [profile.release] 83 | panic = 'unwind' 84 | -------------------------------------------------------------------------------- /node/build.rs: -------------------------------------------------------------------------------- 1 | use vergen::{ConstantsFlags, generate_cargo_keys}; 2 | 3 | const ERROR_MSG: &str = "Failed to generate metadata files"; 4 | 5 | fn main() { 6 | generate_cargo_keys(ConstantsFlags::all()).expect(ERROR_MSG); 7 | println!("cargo:rerun-if-changed=.git/HEAD"); 8 | } 9 | -------------------------------------------------------------------------------- /node/metalog.json: -------------------------------------------------------------------------------- 1 | { 2 | "Metalog": { 3 | "did": "Vec", 4 | "unique_name": "Vec" 5 | } 6 | } -------------------------------------------------------------------------------- /node/runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [features] 2 | default = ['std'] 3 | std = [ 4 | 'parity-codec/std', 5 | 'primitives/std', 6 | 'client/std', 7 | 'rstd/std', 8 | 'runtime-io/std', 9 | 'support/std', 10 | 'balances/std', 11 | 'executive/std', 12 | 'aura/std', 13 | 'indices/std', 14 | 'primitives/std', 15 | 'system/std', 16 | 'timestamp/std', 17 | 'sudo/std', 18 | 'version/std', 19 | 'serde', 20 | 'safe-mix/std', 21 | 'consensus-aura/std', 22 | 'offchain-primitives/std', 23 | ] 24 | [dependencies.aura] 25 | default_features = false 26 | git = 'https://github.com/paritytech/substrate.git' 27 | package = 'srml-aura' 28 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 29 | 30 | [dependencies.balances] 31 | default_features = false 32 | git = 'https://github.com/paritytech/substrate.git' 33 | package = 'srml-balances' 34 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 35 | 36 | [dependencies.client] 37 | default_features = false 38 | git = 'https://github.com/paritytech/substrate.git' 39 | package = 'substrate-client' 40 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 41 | 42 | [dependencies.consensus] 43 | default_features = false 44 | git = 'https://github.com/paritytech/substrate.git' 45 | package = 'srml-consensus' 46 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 47 | 48 | [dependencies.consensus-aura] 49 | default_features = false 50 | git = 'https://github.com/paritytech/substrate.git' 51 | package = 'substrate-consensus-aura-primitives' 52 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 53 | 54 | [dependencies.consensus_authorities] 55 | default-features = false 56 | git = 'https://github.com/paritytech/substrate.git' 57 | package = 'substrate-consensus-authorities' 58 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 59 | 60 | [dependencies.executive] 61 | default_features = false 62 | git = 'https://github.com/paritytech/substrate.git' 63 | package = 'srml-executive' 64 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 65 | 66 | [dependencies.indices] 67 | default_features = false 68 | git = 'https://github.com/paritytech/substrate.git' 69 | package = 'srml-indices' 70 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 71 | 72 | [dependencies.offchain-primitives] 73 | default-features = false 74 | git = 'https://github.com/paritytech/substrate.git' 75 | package = 'substrate-offchain-primitives' 76 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 77 | 78 | [dependencies.parity-codec] 79 | default-features = false 80 | features = ['derive'] 81 | version = '3.5' 82 | 83 | [dependencies.primitives] 84 | default_features = false 85 | git = 'https://github.com/paritytech/substrate.git' 86 | package = 'substrate-primitives' 87 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 88 | 89 | [dependencies.rstd] 90 | default_features = false 91 | git = 'https://github.com/paritytech/substrate.git' 92 | package = 'sr-std' 93 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 94 | 95 | [dependencies.runtime-io] 96 | default_features = false 97 | git = 'https://github.com/paritytech/substrate.git' 98 | package = 'sr-io' 99 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 100 | 101 | [dependencies.runtime-primitives] 102 | default_features = false 103 | git = 'https://github.com/paritytech/substrate.git' 104 | package = 'sr-primitives' 105 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 106 | 107 | [dependencies.safe-mix] 108 | default-features = false 109 | version = '1.0' 110 | 111 | [dependencies.serde] 112 | features = ['derive'] 113 | optional = true 114 | version = '1.0' 115 | 116 | [dependencies.sudo] 117 | default_features = false 118 | git = 'https://github.com/paritytech/substrate.git' 119 | package = 'srml-sudo' 120 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 121 | 122 | [dependencies.support] 123 | default_features = false 124 | git = 'https://github.com/paritytech/substrate.git' 125 | package = 'srml-support' 126 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 127 | 128 | [dependencies.system] 129 | default_features = false 130 | git = 'https://github.com/paritytech/substrate.git' 131 | package = 'srml-system' 132 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 133 | 134 | [dependencies.timestamp] 135 | default_features = false 136 | git = 'https://github.com/paritytech/substrate.git' 137 | package = 'srml-timestamp' 138 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 139 | 140 | [dependencies.version] 141 | default_features = false 142 | git = 'https://github.com/paritytech/substrate.git' 143 | package = 'sr-version' 144 | rev = '783ca1892892454e05e234cda5f7a2e42a54461e' 145 | 146 | [package] 147 | authors = ['Parity Technologies '] 148 | edition = '2018' 149 | name = 'web3-dns-tutorial-runtime' 150 | version = '1.0.0' 151 | -------------------------------------------------------------------------------- /node/runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. 2 | 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | #![cfg_attr(not(feature = "std"), feature(alloc))] 5 | // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. 6 | #![recursion_limit = "256"] 7 | 8 | use client::{ 9 | block_builder::api::{self as block_builder_api, CheckInherentsResult, InherentData}, 10 | impl_runtime_apis, runtime_api, 11 | }; 12 | use parity_codec::{Decode, Encode}; 13 | #[cfg(feature = "std")] 14 | use primitives::bytes; 15 | use primitives::{ed25519, sr25519, OpaqueMetadata}; 16 | use rstd::prelude::*; 17 | use runtime_primitives::{ 18 | create_runtime_str, generic, 19 | traits::{self, BlakeTwo256, Block as BlockT, NumberFor, StaticLookup, Verify}, 20 | transaction_validity::TransactionValidity, 21 | ApplyResult, 22 | }; 23 | #[cfg(feature = "std")] 24 | use serde::{Deserialize, Serialize}; 25 | #[cfg(feature = "std")] 26 | use version::NativeVersion; 27 | use version::RuntimeVersion; 28 | 29 | // A few exports that help ease life for downstream crates. 30 | pub use balances::Call as BalancesCall; 31 | pub use consensus::Call as ConsensusCall; 32 | #[cfg(any(feature = "std", test))] 33 | pub use runtime_primitives::BuildStorage; 34 | pub use runtime_primitives::{Perbill, Permill}; 35 | pub use support::{construct_runtime, StorageValue}; 36 | pub use timestamp::BlockPeriod; 37 | pub use timestamp::Call as TimestampCall; 38 | 39 | /// The type that is used for identifying authorities. 40 | pub type AuthorityId = ::Signer; 41 | 42 | /// The type used by authorities to prove their ID. 43 | pub type AuthoritySignature = ed25519::Signature; 44 | 45 | /// Alias to pubkey that identifies an account on the chain. 46 | pub type AccountId = ::Signer; 47 | 48 | /// The type used by authorities to prove their ID. 49 | pub type AccountSignature = sr25519::Signature; 50 | 51 | /// A hash of some data used by the chain. 52 | pub type Hash = primitives::H256; 53 | 54 | /// Index of a block number in the chain. 55 | pub type BlockNumber = u64; 56 | 57 | /// Index of an account's extrinsic in the chain. 58 | pub type Nonce = u64; 59 | 60 | /// Used for the module template in `./template.rs` 61 | mod template; 62 | 63 | /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know 64 | /// the specifics of the runtime. They can then be made to be agnostic over specific formats 65 | /// of data like extrinsics, allowing for them to continue syncing the network through upgrades 66 | /// to even the core datastructures. 67 | pub mod opaque { 68 | use super::*; 69 | 70 | /// Opaque, encoded, unchecked extrinsic. 71 | #[derive(PartialEq, Eq, Clone, Default, Encode, Decode)] 72 | #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] 73 | pub struct UncheckedExtrinsic(#[cfg_attr(feature = "std", serde(with = "bytes"))] pub Vec); 74 | #[cfg(feature = "std")] 75 | impl std::fmt::Debug for UncheckedExtrinsic { 76 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { 77 | write!(fmt, "{}", primitives::hexdisplay::HexDisplay::from(&self.0)) 78 | } 79 | } 80 | impl traits::Extrinsic for UncheckedExtrinsic { 81 | fn is_signed(&self) -> Option { 82 | None 83 | } 84 | } 85 | /// Opaque block header type. 86 | pub type Header = generic::Header< 87 | BlockNumber, 88 | BlakeTwo256, 89 | generic::DigestItem, 90 | >; 91 | /// Opaque block type. 92 | pub type Block = generic::Block; 93 | /// Opaque block identifier type. 94 | pub type BlockId = generic::BlockId; 95 | /// Opaque session key type. 96 | pub type SessionKey = AuthorityId; 97 | } 98 | 99 | /// This runtime version. 100 | pub const VERSION: RuntimeVersion = RuntimeVersion { 101 | spec_name: create_runtime_str!("web3-dns-tutorial"), 102 | impl_name: create_runtime_str!("web3-dns-tutorial"), 103 | authoring_version: 3, 104 | spec_version: 4, 105 | impl_version: 4, 106 | apis: RUNTIME_API_VERSIONS, 107 | }; 108 | 109 | /// The version infromation used to identify this runtime when compiled natively. 110 | #[cfg(feature = "std")] 111 | pub fn native_version() -> NativeVersion { 112 | NativeVersion { 113 | runtime_version: VERSION, 114 | can_author_with: Default::default(), 115 | } 116 | } 117 | 118 | impl system::Trait for Runtime { 119 | /// The identifier used to distinguish between accounts. 120 | type AccountId = AccountId; 121 | /// The lookup mechanism to get account ID from whatever is passed in dispatchers. 122 | type Lookup = Indices; 123 | /// The index type for storing how many extrinsics an account has signed. 124 | type Index = Nonce; 125 | /// The index type for blocks. 126 | type BlockNumber = BlockNumber; 127 | /// The type for hashing blocks and tries. 128 | type Hash = Hash; 129 | /// The hashing algorithm used. 130 | type Hashing = BlakeTwo256; 131 | /// The header digest type. 132 | type Digest = generic::Digest; 133 | /// The header type. 134 | type Header = generic::Header; 135 | /// The ubiquitous event type. 136 | type Event = Event; 137 | /// The ubiquitous log type. 138 | type Log = Log; 139 | /// The ubiquitous origin type. 140 | type Origin = Origin; 141 | } 142 | 143 | impl aura::Trait for Runtime { 144 | type HandleReport = (); 145 | } 146 | 147 | impl consensus::Trait for Runtime { 148 | /// The identifier we use to refer to authorities. 149 | type SessionKey = AuthorityId; 150 | // The aura module handles offline-reports internally 151 | // rather than using an explicit report system. 152 | type InherentOfflineReport = (); 153 | /// The ubiquitous log type. 154 | type Log = Log; 155 | } 156 | 157 | impl indices::Trait for Runtime { 158 | /// The type for recording indexing into the account enumeration. If this ever overflows, there 159 | /// will be problems! 160 | type AccountIndex = u32; 161 | /// Use the standard means of resolving an index hint from an id. 162 | type ResolveHint = indices::SimpleResolveHint; 163 | /// Determine whether an account is dead. 164 | type IsDeadAccount = Balances; 165 | /// The uniquitous event type. 166 | type Event = Event; 167 | } 168 | 169 | impl timestamp::Trait for Runtime { 170 | /// A timestamp: seconds since the unix epoch. 171 | type Moment = u64; 172 | type OnTimestampSet = Aura; 173 | } 174 | 175 | impl balances::Trait for Runtime { 176 | /// The type for recording an account's balance. 177 | type Balance = u128; 178 | /// What to do if an account's free balance gets zeroed. 179 | type OnFreeBalanceZero = (); 180 | /// What to do if a new account is created. 181 | type OnNewAccount = Indices; 182 | /// The uniquitous event type. 183 | type Event = Event; 184 | 185 | type TransactionPayment = (); 186 | type DustRemoval = (); 187 | type TransferPayment = (); 188 | } 189 | 190 | impl sudo::Trait for Runtime { 191 | /// The uniquitous event type. 192 | type Event = Event; 193 | type Proposal = Call; 194 | } 195 | 196 | /// Used for the module template in `./template.rs` 197 | impl template::Trait for Runtime { 198 | type Event = Event; 199 | } 200 | 201 | construct_runtime!( 202 | pub enum Runtime with Log(InternalLog: DigestItem) where 203 | Block = Block, 204 | NodeBlock = opaque::Block, 205 | UncheckedExtrinsic = UncheckedExtrinsic 206 | { 207 | System: system::{default, Log(ChangesTrieRoot)}, 208 | Timestamp: timestamp::{Module, Call, Storage, Config, Inherent}, 209 | Consensus: consensus::{Module, Call, Storage, Config, Log(AuthoritiesChange), Inherent}, 210 | Aura: aura::{Module}, 211 | Indices: indices, 212 | Balances: balances, 213 | Sudo: sudo, 214 | // Used for the module template in `./template.rs` 215 | TemplateModule: template::{Module, Call, Storage, Event}, 216 | } 217 | ); 218 | 219 | /// The type used as a helper for interpreting the sender of transactions. 220 | type Context = system::ChainContext; 221 | /// The address format for describing accounts. 222 | type Address = ::Source; 223 | /// Block header type as expected by this runtime. 224 | pub type Header = generic::Header; 225 | /// Block type as expected by this runtime. 226 | pub type Block = generic::Block; 227 | /// BlockId type as expected by this runtime. 228 | pub type BlockId = generic::BlockId; 229 | /// Unchecked extrinsic type as expected by this runtime. 230 | pub type UncheckedExtrinsic = 231 | generic::UncheckedMortalCompactExtrinsic; 232 | /// Extrinsic type that has already been checked. 233 | pub type CheckedExtrinsic = generic::CheckedExtrinsic; 234 | /// Executive: handles dispatch to the various modules. 235 | pub type Executive = executive::Executive; 236 | 237 | // Implement our runtime API endpoints. This is just a bunch of proxying. 238 | impl_runtime_apis! { 239 | impl runtime_api::Core for Runtime { 240 | fn version() -> RuntimeVersion { 241 | VERSION 242 | } 243 | 244 | fn execute_block(block: Block) { 245 | Executive::execute_block(block) 246 | } 247 | 248 | fn initialize_block(header: &::Header) { 249 | Executive::initialize_block(header) 250 | } 251 | 252 | fn authorities() -> Vec { 253 | panic!("Deprecated, please use `AuthoritiesApi`.") 254 | } 255 | } 256 | 257 | impl runtime_api::Metadata for Runtime { 258 | fn metadata() -> OpaqueMetadata { 259 | Runtime::metadata().into() 260 | } 261 | } 262 | 263 | impl block_builder_api::BlockBuilder for Runtime { 264 | fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyResult { 265 | Executive::apply_extrinsic(extrinsic) 266 | } 267 | 268 | fn finalize_block() -> ::Header { 269 | Executive::finalize_block() 270 | } 271 | 272 | fn inherent_extrinsics(data: InherentData) -> Vec<::Extrinsic> { 273 | data.create_extrinsics() 274 | } 275 | 276 | fn check_inherents(block: Block, data: InherentData) -> CheckInherentsResult { 277 | data.check_extrinsics(&block) 278 | } 279 | 280 | fn random_seed() -> ::Hash { 281 | System::random_seed() 282 | } 283 | } 284 | 285 | impl runtime_api::TaggedTransactionQueue for Runtime { 286 | fn validate_transaction(tx: ::Extrinsic) -> TransactionValidity { 287 | Executive::validate_transaction(tx) 288 | } 289 | } 290 | 291 | impl consensus_aura::AuraApi for Runtime { 292 | fn slot_duration() -> u64 { 293 | Aura::slot_duration() 294 | } 295 | } 296 | 297 | impl offchain_primitives::OffchainWorkerApi for Runtime { 298 | fn offchain_worker(n: NumberFor) { 299 | Executive::offchain_worker(n) 300 | } 301 | } 302 | 303 | // Needs to be commended out for tests 304 | impl consensus_authorities::AuthoritiesApi for Runtime { 305 | fn authorities() -> Vec { 306 | Consensus::authorities() 307 | } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /node/runtime/src/template.rs: -------------------------------------------------------------------------------- 1 | use parity_codec::{Decode, Encode}; 2 | use rstd::vec::Vec; 3 | use support::{decl_event, decl_module, decl_storage, dispatch::Result, ensure, StorageMap}; 4 | use system::ensure_signed; 5 | 6 | const ERR_DID_ALREADY_CLAIMED: &str = "This DID has already been claimed."; 7 | const ERR_UN_ALREADY_CLAIMED: &str = "This unique name has already been claimed."; 8 | const ERR_OVERFLOW: &str = "Overflow adding new metadata"; 9 | 10 | const ERR_BYTEARRAY_LIMIT_DID: &str = "DID bytearray is too large"; 11 | const ERR_BYTEARRAY_LIMIT_NAME: &str = "Name bytearray is too large"; 12 | 13 | const BYTEARRAY_LIMIT_DID: usize = 100; 14 | const BYTEARRAY_LIMIT_NAME: usize = 50; 15 | 16 | /// The module's configuration trait. 17 | pub trait Trait: system::Trait { 18 | type Event: From> + Into<::Event>; 19 | } 20 | 21 | #[derive(Encode, Decode, Default, Clone, PartialEq)] 22 | #[cfg_attr(feature = "std", derive(Debug))] 23 | pub struct Metalog { 24 | pub did: Vec, 25 | pub unique_name: Vec, 26 | } 27 | 28 | decl_storage! { 29 | trait Store for Module as TemplateModule { 30 | /// Query for unique names 31 | UnMeta get(meta_of_un): map Vec => Metalog; 32 | UnOwner get(owner_of_un): map Vec => Option; 33 | 34 | /// Query by DIDs 35 | DidMeta get(meta_of_did): map Vec => Metalog; 36 | DidOwner get(owner_of_did): map Vec => Option; 37 | 38 | /// Personal owned Metalog data referenced by number 39 | OwnedMetaArray get(metadata_of_owner_by_index): map (T::AccountId, u64) => Metalog; 40 | 41 | /// Number of stored Metalogs per account 42 | OwnedMetaCount get(owner_meta_count): map T::AccountId => u64; 43 | 44 | /// Index of DID 45 | OwnedMetaIndex: map Vec => u64; 46 | } 47 | } 48 | 49 | decl_module! { 50 | /// The module declaration. 51 | pub struct Module for enum Call where origin: T::Origin { 52 | // Initializing events 53 | fn deposit_event() = default; 54 | 55 | fn create_metalog( 56 | origin, 57 | did: Vec, 58 | unique_name: Vec) -> Result { 59 | // Verify 60 | let sender = ensure_signed(origin)?; 61 | ensure!(did.len() <= BYTEARRAY_LIMIT_DID, ERR_BYTEARRAY_LIMIT_DID); 62 | ensure!(unique_name.len() <= BYTEARRAY_LIMIT_NAME, ERR_BYTEARRAY_LIMIT_NAME); 63 | ensure!(!>::exists(&did), ERR_DID_ALREADY_CLAIMED); 64 | ensure!(!>::exists(&unique_name), ERR_UN_ALREADY_CLAIMED); 65 | 66 | let count = Self::owner_meta_count(&sender); 67 | let updated_count = count.checked_add(1).ok_or(ERR_OVERFLOW)?; 68 | 69 | let metalog = Metalog { 70 | did, 71 | unique_name, 72 | }; 73 | 74 | // Store 75 | >::insert((sender.clone(), count), &metalog); 76 | >::insert(&sender, updated_count); 77 | >::insert(&metalog.did, updated_count); 78 | >::insert(&metalog.did, &metalog); 79 | >::insert(&metalog.did, &sender); 80 | >::insert(&metalog.unique_name, &metalog); 81 | >::insert(&metalog.unique_name, &sender); 82 | 83 | Self::deposit_event(RawEvent::Stored(sender, metalog.did, metalog.unique_name)); 84 | Ok(()) 85 | } 86 | } 87 | } 88 | 89 | decl_event!( 90 | pub enum Event 91 | where 92 | AccountId = ::AccountId, 93 | { 94 | Stored(AccountId, Vec, Vec), 95 | } 96 | ); 97 | 98 | /// tests for this module 99 | #[cfg(test)] 100 | mod tests { 101 | use super::*; 102 | 103 | use primitives::{Blake2Hasher, H256}; 104 | use runtime_io::with_externalities; 105 | use runtime_primitives::{ 106 | testing::{Digest, DigestItem, Header}, 107 | traits::{BlakeTwo256, IdentityLookup}, 108 | BuildStorage, 109 | }; 110 | use support::{assert_noop, assert_ok, impl_outer_origin}; 111 | 112 | impl_outer_origin! { 113 | pub enum Origin for Test {} 114 | } 115 | 116 | // For testing the module, we construct most of a mock runtime. This means 117 | // first constructing a configuration type (`Test`) which `impl`s each of the 118 | // configuration traits of modules we want to use. 119 | #[derive(Clone, Eq, PartialEq)] 120 | pub struct Test; 121 | impl system::Trait for Test { 122 | type Origin = Origin; 123 | type Index = u64; 124 | type BlockNumber = u64; 125 | type Hash = H256; 126 | type Hashing = BlakeTwo256; 127 | type Digest = Digest; 128 | type AccountId = u64; 129 | type Lookup = IdentityLookup; 130 | type Header = Header; 131 | type Event = (); 132 | type Log = DigestItem; 133 | } 134 | impl Trait for Test { 135 | type Event = (); 136 | } 137 | type TemplateModule = Module; 138 | 139 | // This function basically just builds a genesis storage key/value store according to 140 | // our desired mockup. 141 | fn new_test_ext() -> runtime_io::TestExternalities { 142 | system::GenesisConfig::::default() 143 | .build_storage() 144 | .unwrap() 145 | .0 146 | .into() 147 | } 148 | 149 | #[test] 150 | fn create_metalog_works() { 151 | with_externalities(&mut new_test_ext(), || { 152 | let did = vec![1, 2]; 153 | let un = vec![1]; 154 | let mut did_too_long = did.clone(); 155 | let mut un_too_long = un.clone(); 156 | for _i in 1..100 { 157 | did_too_long.push(2); 158 | un_too_long.push(1); 159 | } 160 | assert_noop!( 161 | TemplateModule::create_metalog( 162 | Origin::signed(20), 163 | did_too_long.clone(), 164 | un.clone() 165 | ), 166 | ERR_BYTEARRAY_LIMIT_DID 167 | ); 168 | assert_noop!( 169 | TemplateModule::create_metalog( 170 | Origin::signed(20), 171 | did.clone(), 172 | un_too_long.clone() 173 | ), 174 | ERR_BYTEARRAY_LIMIT_NAME 175 | ); 176 | assert_ok!(TemplateModule::create_metalog( 177 | Origin::signed(20), 178 | did.clone(), 179 | un.clone() 180 | )); 181 | assert_eq!(TemplateModule::owner_of_did(did), Some(20)); 182 | }); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /node/runtime/wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.release] 2 | lto = true 3 | panic = 'abort' 4 | [dependencies.web3-dns-tutorial-runtime] 5 | default-features = false 6 | path = '..' 7 | 8 | [features] 9 | default = [] 10 | std = ['web3-dns-tutorial-runtime/std'] 11 | 12 | [lib] 13 | crate-type = ['cdylib'] 14 | 15 | [workspace] 16 | members = [] 17 | 18 | [package] 19 | authors = ['Parity Technologies '] 20 | edition = '2018' 21 | name = 'web3-dns-tutorial-runtime-wasm' 22 | version = '1.0.0' 23 | -------------------------------------------------------------------------------- /node/runtime/wasm/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | if cargo --version | grep -q "nightly"; then 5 | CARGO_CMD="cargo" 6 | else 7 | CARGO_CMD="cargo +nightly" 8 | fi 9 | CARGO_INCREMENTAL=0 RUSTFLAGS="-C link-arg=--export-table" $CARGO_CMD build --target=wasm32-unknown-unknown --release 10 | for i in web3_dns_tutorial_runtime_wasm 11 | do 12 | wasm-gc target/wasm32-unknown-unknown/release/$i.wasm target/wasm32-unknown-unknown/release/$i.compact.wasm 13 | done 14 | -------------------------------------------------------------------------------- /node/runtime/wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The Substrate node template runtime reexported for WebAssembly compile. 2 | 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | 5 | pub use web3_dns_tutorial_runtime::*; 6 | -------------------------------------------------------------------------------- /node/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." >/dev/null && pwd )" 6 | 7 | export CARGO_INCREMENTAL=0 8 | 9 | bold=$(tput bold) 10 | normal=$(tput sgr0) 11 | 12 | # Save current directory. 13 | pushd . >/dev/null 14 | 15 | for SRC in runtime/wasm 16 | do 17 | echo "${bold}Building webassembly binary in $SRC...${normal}" 18 | cd "$PROJECT_ROOT/$SRC" 19 | 20 | ./build.sh 21 | 22 | cd - >> /dev/null 23 | done 24 | 25 | # Restore initial directory. 26 | popd >/dev/null 27 | -------------------------------------------------------------------------------- /node/scripts/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 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 | 14 | # Install wasm-gc. It's useful for stripping slimming down wasm binaries. 15 | command -v wasm-gc || \ 16 | cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force 17 | -------------------------------------------------------------------------------- /node/src/chain_spec.rs: -------------------------------------------------------------------------------- 1 | use primitives::{ed25519, sr25519, Pair}; 2 | use web3_dns_tutorial_runtime::{ 3 | AccountId, GenesisConfig, ConsensusConfig, TimestampConfig, BalancesConfig, 4 | SudoConfig, IndicesConfig, 5 | }; 6 | use substrate_service; 7 | 8 | use ed25519::Public as AuthorityId; 9 | 10 | // Note this is the URL for the telemetry server 11 | //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; 12 | 13 | /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. 14 | pub type ChainSpec = substrate_service::ChainSpec; 15 | 16 | /// The chain specification option. This is expected to come in from the CLI and 17 | /// is little more than one of a number of alternatives which can easily be converted 18 | /// from a string (`--chain=...`) into a `ChainSpec`. 19 | #[derive(Clone, Debug)] 20 | pub enum Alternative { 21 | /// Whatever the current runtime is, with just Alice as an auth. 22 | Development, 23 | /// Whatever the current runtime is, with simple Alice/Bob auths. 24 | LocalTestnet, 25 | } 26 | 27 | fn authority_key(s: &str) -> AuthorityId { 28 | ed25519::Pair::from_string(&format!("//{}", s), None) 29 | .expect("static values are valid; qed") 30 | .public() 31 | } 32 | 33 | fn account_key(s: &str) -> AccountId { 34 | sr25519::Pair::from_string(&format!("//{}", s), None) 35 | .expect("static values are valid; qed") 36 | .public() 37 | } 38 | 39 | impl Alternative { 40 | /// Get an actual chain config from one of the alternatives. 41 | pub(crate) fn load(self) -> Result { 42 | Ok(match self { 43 | Alternative::Development => ChainSpec::from_genesis( 44 | "Development", 45 | "dev", 46 | || testnet_genesis(vec![ 47 | authority_key("Alice") 48 | ], vec![ 49 | account_key("Alice") 50 | ], 51 | account_key("Alice") 52 | ), 53 | vec![], 54 | None, 55 | None, 56 | None, 57 | None 58 | ), 59 | Alternative::LocalTestnet => ChainSpec::from_genesis( 60 | "Local Testnet", 61 | "local_testnet", 62 | || testnet_genesis(vec![ 63 | authority_key("Alice"), 64 | authority_key("Bob"), 65 | ], vec![ 66 | account_key("Alice"), 67 | account_key("Bob"), 68 | account_key("Charlie"), 69 | account_key("Dave"), 70 | account_key("Eve"), 71 | account_key("Ferdie"), 72 | ], 73 | account_key("Alice"), 74 | ), 75 | vec![], 76 | None, 77 | None, 78 | None, 79 | None 80 | ), 81 | }) 82 | } 83 | 84 | pub(crate) fn from(s: &str) -> Option { 85 | match s { 86 | "dev" => Some(Alternative::Development), 87 | "" | "local" => Some(Alternative::LocalTestnet), 88 | _ => None, 89 | } 90 | } 91 | } 92 | 93 | fn testnet_genesis(initial_authorities: Vec, endowed_accounts: Vec, root_key: AccountId) -> GenesisConfig { 94 | GenesisConfig { 95 | consensus: Some(ConsensusConfig { 96 | code: include_bytes!("../runtime/wasm/target/wasm32-unknown-unknown/release/web3_dns_tutorial_runtime_wasm.compact.wasm").to_vec(), 97 | authorities: initial_authorities.clone(), 98 | }), 99 | system: None, 100 | timestamp: Some(TimestampConfig { 101 | minimum_period: 5, // 10 second block time. 102 | }), 103 | indices: Some(IndicesConfig { 104 | ids: endowed_accounts.clone(), 105 | }), 106 | balances: Some(BalancesConfig { 107 | transaction_base_fee: 1, 108 | transaction_byte_fee: 0, 109 | existential_deposit: 500, 110 | transfer_fee: 0, 111 | creation_fee: 0, 112 | balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), 113 | vesting: vec![], 114 | }), 115 | sudo: Some(SudoConfig { 116 | key: root_key, 117 | }), 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /node/src/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::service; 2 | use futures::{future, Future, sync::oneshot}; 3 | use std::cell::RefCell; 4 | use tokio::runtime::Runtime; 5 | pub use substrate_cli::{VersionInfo, IntoExit, error}; 6 | use substrate_cli::{informant, parse_and_execute, NoCustom}; 7 | use substrate_service::{ServiceFactory, Roles as ServiceRoles}; 8 | use crate::chain_spec; 9 | use std::ops::Deref; 10 | use log::info; 11 | 12 | /// Parse command line arguments into service configuration. 13 | pub fn run(args: I, exit: E, version: VersionInfo) -> error::Result<()> where 14 | I: IntoIterator, 15 | T: Into + Clone, 16 | E: IntoExit, 17 | { 18 | parse_and_execute::( 19 | load_spec, &version, "substrate-node", args, exit, 20 | |exit, _custom_args, config| { 21 | info!("{}", version.name); 22 | info!(" version {}", config.full_version()); 23 | info!(" by {}, 2017, 2018", version.author); 24 | info!("Chain specification: {}", config.chain_spec.name()); 25 | info!("Node name: {}", config.name); 26 | info!("Roles: {:?}", config.roles); 27 | let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?; 28 | let executor = runtime.executor(); 29 | match config.roles { 30 | ServiceRoles::LIGHT => run_until_exit( 31 | runtime, 32 | service::Factory::new_light(config, executor).map_err(|e| format!("{:?}", e))?, 33 | exit 34 | ), 35 | _ => run_until_exit( 36 | runtime, 37 | service::Factory::new_full(config, executor).map_err(|e| format!("{:?}", e))?, 38 | exit 39 | ), 40 | }.map_err(|e| format!("{:?}", e)) 41 | } 42 | ).map_err(Into::into).map(|_| ()) 43 | } 44 | 45 | fn load_spec(id: &str) -> Result, String> { 46 | Ok(match chain_spec::Alternative::from(id) { 47 | Some(spec) => Some(spec.load()?), 48 | None => None, 49 | }) 50 | } 51 | 52 | fn run_until_exit( 53 | mut runtime: Runtime, 54 | service: T, 55 | e: E, 56 | ) -> error::Result<()> 57 | where 58 | T: Deref>, 59 | C: substrate_service::Components, 60 | E: IntoExit, 61 | { 62 | let (exit_send, exit) = exit_future::signal(); 63 | 64 | let executor = runtime.executor(); 65 | informant::start(&service, exit.clone(), executor.clone()); 66 | 67 | let _ = runtime.block_on(e.into_exit()); 68 | exit_send.fire(); 69 | 70 | // we eagerly drop the service so that the internal exit future is fired, 71 | // but we need to keep holding a reference to the global telemetry guard 72 | let _telemetry = service.telemetry(); 73 | drop(service); 74 | Ok(()) 75 | } 76 | 77 | // handles ctrl-c 78 | pub struct Exit; 79 | impl IntoExit for Exit { 80 | type Exit = future::MapErr, fn(oneshot::Canceled) -> ()>; 81 | fn into_exit(self) -> Self::Exit { 82 | // can't use signal directly here because CtrlC takes only `Fn`. 83 | let (exit_send, exit) = oneshot::channel(); 84 | 85 | let exit_send_cell = RefCell::new(Some(exit_send)); 86 | ctrlc::set_handler(move || { 87 | if let Some(exit_send) = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take() { 88 | exit_send.send(()).expect("Error sending exit notification"); 89 | } 90 | }).expect("Error setting Ctrl-C handler"); 91 | 92 | exit.map_err(drop) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /node/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Initialization errors. 2 | 3 | use client; 4 | 5 | error_chain! { 6 | foreign_links { 7 | Io(::std::io::Error) #[doc="IO error"]; 8 | Cli(::clap::Error) #[doc="CLI error"]; 9 | } 10 | links { 11 | Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /node/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Substrate Node Template CLI library. 2 | 3 | #![warn(missing_docs)] 4 | #![warn(unused_extern_crates)] 5 | 6 | mod chain_spec; 7 | mod service; 8 | mod cli; 9 | 10 | pub use substrate_cli::{VersionInfo, IntoExit, error}; 11 | 12 | fn run() -> cli::error::Result<()> { 13 | let version = VersionInfo { 14 | name: "Substrate Node", 15 | commit: env!("VERGEN_SHA_SHORT"), 16 | version: env!("CARGO_PKG_VERSION"), 17 | executable_name: "web3-dns-tutorial", 18 | author: "pact", 19 | description: "web3-dns-tutorial", 20 | support_url: "support.anonymous.an", 21 | }; 22 | cli::run(::std::env::args(), cli::Exit, version) 23 | } 24 | 25 | error_chain::quick_main!(run); 26 | -------------------------------------------------------------------------------- /node/src/service.rs: -------------------------------------------------------------------------------- 1 | //! Service and ServiceFactory implementation. Specialized wrapper over Substrate service. 2 | 3 | #![warn(unused_extern_crates)] 4 | 5 | use std::sync::Arc; 6 | use log::info; 7 | use transaction_pool::{self, txpool::{Pool as TransactionPool}}; 8 | use web3_dns_tutorial_runtime::{self, GenesisConfig, opaque::Block, RuntimeApi}; 9 | use substrate_service::{ 10 | FactoryFullConfiguration, LightComponents, FullComponents, FullBackend, 11 | FullClient, LightClient, LightBackend, FullExecutor, LightExecutor, 12 | TaskExecutor, 13 | }; 14 | use basic_authorship::ProposerFactory; 15 | use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration, NothingExtra}; 16 | use substrate_client as client; 17 | use primitives::{ed25519::Pair, Pair as PairT}; 18 | use inherents::InherentDataProviders; 19 | use network::construct_simple_protocol; 20 | use substrate_executor::native_executor_instance; 21 | use substrate_service::construct_service_factory; 22 | 23 | pub use substrate_executor::NativeExecutor; 24 | // Our native executor instance. 25 | native_executor_instance!( 26 | pub Executor, 27 | web3_dns_tutorial_runtime::api::dispatch, 28 | web3_dns_tutorial_runtime::native_version, 29 | include_bytes!("../runtime/wasm/target/wasm32-unknown-unknown/release/web3_dns_tutorial_runtime_wasm.compact.wasm") 30 | ); 31 | 32 | #[derive(Default)] 33 | pub struct NodeConfig { 34 | inherent_data_providers: InherentDataProviders, 35 | } 36 | 37 | construct_simple_protocol! { 38 | /// Demo protocol attachment for substrate. 39 | pub struct NodeProtocol where Block = Block { } 40 | } 41 | 42 | construct_service_factory! { 43 | struct Factory { 44 | Block = Block, 45 | RuntimeApi = RuntimeApi, 46 | NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, 47 | RuntimeDispatch = Executor, 48 | FullTransactionPoolApi = transaction_pool::ChainApi, FullExecutor, Block, RuntimeApi>, Block> 49 | { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) }, 50 | LightTransactionPoolApi = transaction_pool::ChainApi, LightExecutor, Block, RuntimeApi>, Block> 51 | { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) }, 52 | Genesis = GenesisConfig, 53 | Configuration = NodeConfig, 54 | FullService = FullComponents 55 | { |config: FactoryFullConfiguration, executor: TaskExecutor| 56 | FullComponents::::new(config, executor) 57 | }, 58 | AuthoritySetup = { 59 | |service: Self::FullService, executor: TaskExecutor, key: Option>| { 60 | if let Some(key) = key { 61 | info!("Using authority key {}", key.public()); 62 | let proposer = Arc::new(ProposerFactory { 63 | client: service.client(), 64 | transaction_pool: service.transaction_pool(), 65 | inherents_pool: service.inherents_pool(), 66 | }); 67 | let client = service.client(); 68 | executor.spawn(start_aura( 69 | SlotDuration::get_or_compute(&*client)?, 70 | key.clone(), 71 | client.clone(), 72 | client, 73 | proposer, 74 | service.network(), 75 | service.on_exit(), 76 | service.config.custom.inherent_data_providers.clone(), 77 | service.config.force_authoring, 78 | )?); 79 | } 80 | 81 | Ok(service) 82 | } 83 | }, 84 | LightService = LightComponents 85 | { |config, executor| >::new(config, executor) }, 86 | FullImportQueue = AuraImportQueue< 87 | Self::Block, 88 | > 89 | { |config: &mut FactoryFullConfiguration , client: Arc>| { 90 | import_queue::<_, _, _, Pair>( 91 | SlotDuration::get_or_compute(&*client)?, 92 | client.clone(), 93 | None, 94 | client, 95 | NothingExtra, 96 | config.custom.inherent_data_providers.clone(), 97 | ).map_err(Into::into) 98 | } 99 | }, 100 | LightImportQueue = AuraImportQueue< 101 | Self::Block, 102 | > 103 | { |config: &mut FactoryFullConfiguration, client: Arc>| { 104 | import_queue::<_, _, _, Pair>( 105 | SlotDuration::get_or_compute(&*client)?, 106 | client.clone(), 107 | None, 108 | client, 109 | NothingExtra, 110 | config.custom.inherent_data_providers.clone(), 111 | ).map_err(Into::into) 112 | } 113 | }, 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /tutorial/1_introduction.md: -------------------------------------------------------------------------------- 1 | # 1. Introduction 2 | 3 | With this tutorial we will take you through the process of easily building your own blockchain using [Substrate](https://github.com/paritytech/substrate), an open source Rust Blockchain Development Kit by Parity. More specificaly, the goal is to create a simple domain name runtime module for the next generation of web. 4 | 5 | A runtime is the block execution logic of the blockchain and consists of different runtime modules. A module typically consists of storage items, functions, and events to enable a certain set of features. The following are the modules that ship with the Substrate Runtime Module Library (SRML). 6 | 7 | * [Assets](https://crates.parity.io/srml_assets/index.html) 8 | * [Aura](https://crates.parity.io/srml_aura/index.html) 9 | * [Balances](https://crates.parity.io/srml_balances/index.html) 10 | * [Consensus](https://crates.parity.io/srml_consensus/index.html) 11 | * [Contract](https://crates.parity.io/srml_contract/index.html) 12 | * [Council](https://crates.parity.io/srml_council/index.html) 13 | * [Democracy]() 14 | * [Finality Tracker](https://crates.parity.io/srml_democracy/index.html) 15 | * [Grandpa](https://crates.parity.io/srml_grandpa/index.html) 16 | * [Indices](https://crates.parity.io/srml_indices/index.html) 17 | * [Session](https://crates.parity.io/srml_session/index.html) 18 | * [Staking](https://crates.parity.io/srml_staking/index.html) 19 | * [Sudo](https://crates.parity.io/srml_sudo/index.html) 20 | * [Timestamp](https://crates.parity.io/srml_timestamp/index.html) 21 | * [Treasury](https://crates.parity.io/srml_treasury/index.html) 22 | 23 | Don’t worry if you have never used Rust before. If you have some programming experience, you will be able to follow the tutorial. Rust is a strongly typed programming language, which you’ll love to hate and will help you to build a reliable and efficient software. If you want to get a better understanding of Rust, feel free to take a look at the [official website](https://www.rust-lang.org/). This source also provides great Rust tutorials and books. 24 | 25 | In case you want to skip the tutorial or simply check certain things, you can find the finalized runtime module in the node folder of this repository. 26 | 27 | **-> [Next: 2. Setup](./2_setup.md)** 28 | -------------------------------------------------------------------------------- /tutorial/2_setup.md: -------------------------------------------------------------------------------- 1 | # 2. Setup 2 | 3 | First, you need to install Rust and its additional dependencies, which might take a while (~20 minutes). If you’re on a Mac or Linux machine, you can do this with the following one-line command: 4 | 5 | ```bash 6 | curl https://getsubstrate.io -sSf | bash -s -- --fast 7 | ``` 8 | 9 | If you are using Windows you'll probably want to install a Bash shell, as well as take a look at the following [instructions](https://github.com/paritytech/substrate#612-windows). 10 | When everything is ready, you might want to restart your terminal. Once this is done, you can create your own node template folder by running: 11 | 12 | ```bash 13 | substrate-node-new 14 | ``` 15 | 16 | Now, make sure you switch to this folder. If you want to run your blockchain, you'll always need to compile your Wasm image first and then you can build the executable. Additionally, you'll probably want to clean the cache of your previous run. 17 | 18 | ```bash 19 | ./scripts/build.sh 20 | cargo build --release 21 | ./target/release/ purge-chain –dev 22 | ``` 23 | 24 | All that is left to do is to finally start your own blockchain: 25 | 26 | ```bash 27 | ./target/release/ --dev 28 | ``` 29 | 30 | After this, you might need to agree to the firewall settings and you should see something similiar to the following image: 31 | 32 | 33 | 34 | **-> [Next: 3. Interface](./3_interface.md)** 35 | -------------------------------------------------------------------------------- /tutorial/3_interface.md: -------------------------------------------------------------------------------- 1 | # 3. Interface 2 | 3 | Parity provides a nice graphical interface to interact with your blockchain, which doesn’t require you to code your own interface immediately. It’s the [Polkadot JS UI for Substrate]( https://polkadot.js.org/apps/), which only works in [Chrome](https://www.google.com/chrome/) or on a Chromium-based browser. 4 | 5 | With your blockchain running in the console, you can open the website. Make sure to select the settings tab. On this tab, choose Local Node (127.0.0.1:9944) as well as Substrate as your interface, as shown in the image below. 6 | 7 | 8 | 9 | Once you have done this, you can click on Save and Reload and start exploring your blockchain. The explorer tab shows the recent blocks produced, and the accounts are listed in the accounts tab. If you want to make your first transfer, you can select the transfer tab. Make sure to select Alice as the sender and someone else as the receiver. Alice is a pre-funded account. The amount you transfer needs to be higher than the existential deposit (the minimum amount an account needs to hold to be part of the system). Additionally, a fixed fee is added to the transaction. Keep in mind that these are the default settings and that you can change them very easily in the [src/chain_spec.rs file](../node/src/chain_spec.rs). If all is correct it should look similar to the following, and you can click on “Make Transfer”: 10 | 11 | 12 | 13 | The most important tabs for the development process are the chain state and extrinsics tabs. For both, you can either select the predefined or your own runtime modules. The chain state tab lets you access the storage, and the extrinsics tab provides you access to the public methods of the modules. 14 | 15 | 16 | 17 | Feel free to explore the different modules. 18 | 19 | **-> [Next: 4. Runtime](./4_runtime.md)** 20 | -------------------------------------------------------------------------------- /tutorial/4_runtime.md: -------------------------------------------------------------------------------- 1 | # 4. Runtime 2 | 3 | Let’s now start creating your own runtime module. All runtime modules are registered in the [lib.rs file]( ../node/runtime/src/lib.rs). You'll also find in this file the registration of some of the previously mentioned runtime modules, like Consensus or Balances. If you want, you can change the default modules or integrate other modules from the Substrate Runtime Module Library (SRML). But for now, let’s leave everything as it is and just add an additional module. 4 | 5 | Luckily, the [lib.rs file]( ../node/runtime/src/lib.rs) and the folder already contain a [template.rs file]( ../node/runtime/src/template.rs) which we can use. If you want, you can change the name of the file. If you do this, make sure you also change the three appearances of this file inside [lib.rs file]( ../node/runtime/src/lib.rs), which are: 6 | 7 | * Import of the file ("mod template;") 8 | * Implementation of the traits ("impl template::Trait for Runtime{...") 9 | * Integration of the module into the construct_runtime! macro ("TemplateModule: template::{Module,...") 10 | 11 | Next, we are going to add a storage item, a function as well as an event to the [template.rs file]( ../node/runtime/src/template.rs). Therefore, we are going to edit the “Something” in the decl_storage, decl_module as well as decl_event part. 12 | 13 | ## Storage 14 | 15 | The “Something get(something)” element in the template file stores just a single value. With the next generation of DNS, we'll probably want to store a more complex structure. That’s why we first create a struct, which we call Metalog: 16 | 17 | ``` 18 | #[derive(Encode, Decode, Default, Clone, PartialEq)] 19 | #[cfg_attr(feature = "std", derive(Debug))] 20 | pub struct Metalog { 21 | pub did: Vec, 22 | pub unique_name: Vec, 23 | } 24 | ``` 25 | DID stands for [decentralized identifier](https://w3c-ccg.github.io/did-spec/) and it points to a document containing all the information about the data as well as storage location. The unique name is the equivalent to the current domain names and is a simple, human-readable name. To define this custom struct, we'll also need to import additional traits at the top of the document: 26 | ``` 27 | use parity_codec::{Encode, Decode}; 28 | use rstd::vec::Vec; 29 | ``` 30 | The import of rstd::vec::Vec is necessary to support byte vectors, which will essentially be used to handle strings in the module. 31 | 32 | You'll probably also want to map the individual domains names to specific accounts instead of storing them across all accounts. That’s why we'll replace the StorageValue import with the StorageMap import. Additionally, we'll add the ensure trait. This is used to test certain requirements before the variable is stored. 33 | ``` 34 | use support::{decl_module, decl_storage, decl_event, ensure, StorageMap, dispatch::Result}; 35 | ``` 36 | Finally, we'll create the storage mapping inside the decl_storage! module. 37 | ``` 38 | trait Store for Module as TemplateModule { 39 | /// Query for unique names 40 | UnMeta get(meta_of_un): map Vec => Metalog; 41 | UnOwner get(owner_of_un): map Vec => Option; 42 | 43 | /// Query by DIDs 44 | DidMeta get(meta_of_did): map Vec => Metalog; 45 | DidOwner get(owner_of_did): map Vec => Option; 46 | 47 | /// Personal owned Metalog data referenced by number 48 | OwnedMetaArray get(metadata_of_owner_by_index): map (T::AccountId, u64) => Metalog; 49 | 50 | /// Number of stored Metalogs per account 51 | OwnedMetaCount get(owner_meta_count): map T::AccountId => u64; 52 | 53 | /// Index of DID 54 | OwnedMetaIndex: map Vec => u64; 55 | } 56 | ``` 57 | The last three elements are especially interesting since they basically represent an array based on the combination of tuples and maps. This enables every user to own multiple metalog entries. 58 | 59 | ## Function 60 | Now, we're actually going to implement a function to create a metalog entry. Therefore, we'll put the following function inside “pub struct Module for enum Call where origin: T::Origin {“. 61 | ``` 62 | /// Store initial metalog 63 | fn create_metalog( 64 | origin, 65 | did: Vec, 66 | unique_name: Vec) -> Result { 67 | // Verify 68 | let sender = ensure_signed(origin)?; 69 | ensure!(did.len() <= BYTEARRAY_LIMIT_DID, ERR_BYTEARRAY_LIMIT_DID); 70 | ensure!(unique_name.len() <= BYTEARRAY_LIMIT_NAME, ERR_BYTEARRAY_LIMIT_NAME); 71 | ensure!(!>::exists(&did), ERR_DID_ALREADY_CLAIMED); 72 | ensure!(!>::exists(&unique_name), ERR_UN_ALREADY_CLAIMED); 73 | 74 | let count = Self::owner_meta_count(&sender); 75 | let updated_count = count.checked_add(1).ok_or(ERR_OVERFLOW)?; 76 | 77 | let metalog = Metalog { 78 | did, 79 | unique_name, 80 | }; 81 | 82 | // Store 83 | >::insert((sender.clone(), count), &metalog); 84 | >::insert(&sender, updated_count); 85 | >::insert(&metalog.did, updated_count); 86 | >::insert(&metalog.did, &metalog); 87 | >::insert(&metalog.did, &sender); 88 | >::insert(&metalog.unique_name, &metalog); 89 | >::insert(&metalog.unique_name, &sender); 90 | 91 | Self::deposit_event(RawEvent::Stored(sender, metalog.did, metalog.unique_name)); 92 | Ok(()) 93 | } 94 | ``` 95 | It’s very important to always stick to the pattern **"Verify First, Store Last"**, as your Runtime should never panic and should also be safe against potential attacks. Panics can completely destroy your blockchain storage. That’s why, at the beginning of the function, we'll use multiple ensure! checks. Typical verifications are: 96 | 97 | * Verifying Signed Messages 98 | * Overflows/Underflows Checks 99 | * Collision Checks 100 | * Storage Limit 101 | 102 | After the checks we'll store the values. 103 | 104 | ## Events 105 | 106 | You might have noticed that at the end of the above function we implemented a call to our event. This ensures that we tell the world that the function executed successfully. Now, all that is left to do is to declare the actual event in the decl_event! module. 107 | 108 | ``` 109 | pub enum Event 110 | where 111 | AccountId = ::AccountId, 112 | { 113 | Stored(AccountId, Vec, Vec), 114 | } 115 | ``` 116 | 117 | Obviously, the runtime module is only a starting point for a Web3 Domain Name/Metadata System. You'll probably want to at least add functions to update the metalog entry. Feel free to take a look at the [Stars Network Whitepaper](https://github.com/PACTCare/Stars-Network/blob/master/WHITEPAPER.md) and specifically at the [Starlog runtime](https://github.com/PACTCare/Starlog). 118 | 119 | 120 | **-> [Next: 5. Testing](./5_testing.md)** 121 | -------------------------------------------------------------------------------- /tutorial/5_testing.md: -------------------------------------------------------------------------------- 1 | # 5. Testing 2 | 3 | Now let’s compile the runtime and run it. As a reminder, here are the commands to do this: 4 | 5 | ```bash 6 | ./scripts/build.sh 7 | cargo build --release 8 | ./target/release/ purge-chain –dev 9 | ./target/release/ --dev 10 | ``` 11 | 12 | Once the blockchain is running, open the [Polkadot JS UI for Substrate](https://polkadot.js.org/apps/) in your Chromium-based browser again. The first thing you'll need to do is to register the following custom struct with a JSON file or by copy pasting it: 13 | 14 | ``` 15 | { 16 | "Metalog": { 17 | "did": "Vec", 18 | "unique_name": "Vec" 19 | } 20 | } 21 | ``` 22 | You can do this by clicking on the settings tab and then selecting the horizontal developer tab (see image below). 23 | 24 | 25 | 26 | Next, let's head to the extrinsic tab. Select the name of your runtime module and then select the createMetalog method. Make sure you have also selected an account which can pay the transaction fee, which should usually be Alice. Next, post the following text in both fields: 27 | 28 | ``` 29 | 0x48656c6c6f20576f726c64 30 | ``` 31 | This stands for “Hello World” and is based on ["Simple Concatenated Aggregate Little-Endian" (SCALE)](https://substrate.dev/docs/en/overview/low-level-data-format) encoding. You should now be able to “Sign and Submit” the transaction and on the right side of your screen, you should see the events popping up. 32 | 33 | 34 | 35 | If you try to send the exact same transaction again, you should see a red event popping up and, in the terminal, you should see the following error message. 36 | 37 | 38 | 39 | Next, you can open the chain state tab and query the different storage parts. You can open the storage by pressing the blue plus button on the right side. The image below shows an example query of the DID. 40 | 41 | 42 | 43 | **-> [Next: 6. Writing Tests](./6_unit_tests.md)** 44 | -------------------------------------------------------------------------------- /tutorial/6_unit_tests.md: -------------------------------------------------------------------------------- 1 | # 6. Unit Tests 2 | 3 | In this section, we will start writing a very simple unit tests for our runtime. Keep in mind that if you ever want to release a runtime as part of an active blockchain, you want to do a lot more than just unit tests (e.g., integration, system tests, security audit, etc.). 4 | 5 | Luckily the template file also contains a basic test setup at the bottom of [template.rs file]( ../node/runtime/src/template.rs). At the beginning, inside mod tests, there are some imports which will help us to create a mock runtime. Here, we are going to add the “assert_noop” import after assert_ok. 6 | The actual testing function is at the very end and should look like this: 7 | ``` 8 | #[test] 9 | fn it_works_for_default_value() { 10 | with_externalities(&mut new_test_ext(), || { 11 | assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); 12 | assert_eq!(TemplateModule::something(), Some(42)); 13 | }); 14 | } 15 | ``` 16 | The first thing you'll probably want to do is to rename this function to something like "create_metalog_works()". 17 | 18 | When writing runtime tests, it makes sense to follow the [equivalence class partitioning technique]( https://en.wikipedia.org/wiki/Equivalence_partitioning) to derive test cases, and you want to write at least one test for each equivalence class. Ideally, you test the interior as well as the boundary values of each equivalence class. To determine the different equivalence classes in this example, you can take a look at different the ensure statements. 19 | 20 | If you are unfamiliar with equivalence class testing, you can also just follow the NOE-Rule (assert_noop + assert_ok + assert_eq). This means that you first write the assert_noop tests inside the test function. With these macros, you essentially test all the ensure cases, which cause your function to fail. Next, you can use assert_ok to test a correct execution of your function, and finally, you can test if the result is stored correctly with assert_eq. A testing function could for example look like this: 21 | ``` 22 | #[test] 23 | fn create_metalog_works() { 24 | with_externalities(&mut new_test_ext(), || { 25 | let did = vec![1, 2]; 26 | let un = vec![1]; 27 | let mut did_too_long = did.clone(); 28 | let mut un_too_long = un.clone(); 29 | for _i in 1..100 { 30 | did_too_long.push(2); 31 | un_too_long.push(1); 32 | } 33 | assert_noop!( 34 | TemplateModule::create_metalog( 35 | Origin::signed(20), 36 | did_too_long.clone(), 37 | un.clone() 38 | ), 39 | ERR_BYTEARRAY_LIMIT_DID 40 | ); 41 | assert_noop!( 42 | TemplateModule::create_metalog( 43 | Origin::signed(20), 44 | did.clone(), 45 | un_too_long.clone() 46 | ), 47 | ERR_BYTEARRAY_LIMIT_NAME 48 | ); 49 | assert_ok!(TemplateModule::create_metalog( 50 | Origin::signed(20), 51 | did.clone(), 52 | un.clone() 53 | )); 54 | assert_eq!(TemplateModule::owner_of_did(did), Some(20)); 55 | }); 56 | } 57 | ``` 58 | Be aware that the above function is just an example and neither tests all equivalence classes nor the interior as well as boundary values. Feel free to write additional tests as an exercise. 59 | 60 | For the tests to past you'll also want to comment the following at the very end of the lib.rs file: 61 | ``` 62 | // impl consensus_authorities::AuthoritiesApi for Runtime { 63 | // fn authorities() -> Vec { 64 | // Consensus::authorities() 65 | // } 66 | // } 67 | ``` 68 | To run the tests, you'll need to first switch to the runtime folder inside your terminal and then run the following command: 69 | ``` 70 | cargo test 71 | ``` 72 | If everything works correctly you should see something similar to the following on your screen: 73 | 74 | 75 | 76 | **-> [Next: 7. UI](./7_ui.md)** 77 | -------------------------------------------------------------------------------- /tutorial/7_ui.md: -------------------------------------------------------------------------------- 1 | # 7. UI 2 | 3 | For the UI, we will use npm and [webpack](https://webpack.js.org/). To get started, create a project folder and run the following commands inside this folder in your terminal. 4 | 5 | ``` 6 | npm init -y 7 | npm install webpack --save-dev 8 | npm install webpack-cli --save-dev 9 | ``` 10 | Next, create an [index.html](../ui/dist/index.html) file, a [style.css](../ui/css/style.css) file inside a [css](../ui/css/style.css) folder as well as an [index.js file](../ui/src/index.js) inside an src folder. Additionally, you'll want to create a webpack.config.js, which basically tells webpack what to do. For this tutorial we will focus on the [index.js file](../ui/src/index.js) file, so feel free to copy the content of the [html](../ui/dist/index.html), [css](../ui/css/style.css) and [webpack.config.js](../ui/webpack.config.js) file from the UI folder of this GitHub repro. 11 | 12 | You will also need to change the scripts' part of the package.json file to the following: 13 | ``` 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1", 16 | "build": "webpack" 17 | }, 18 | ``` 19 | 20 | Now, we'll install the packages to interact with our substrate runtime: 21 | ``` 22 | npm i @polkadot/api@0.82.0-beta.9 23 | npm i @polkadot/keyring@0.94.0-beta.3 24 | ``` 25 | and import these packages at the top of our js file as well as define two constants. The constant represents the WebSocket endpoint. 26 | ``` 27 | import { ApiPromise, WsProvider } from '@polkadot/api'; 28 | import { Keyring } from '@polkadot/keyring'; 29 | 30 | const provider = new WsProvider('ws://127.0.0.1:9944'); 31 | ``` 32 | Next, we'll define a small substrate function: 33 | ``` 34 | async function substrate() { 35 | console.log('connect...'); 36 | const api = await ApiPromise.create({ 37 | provider: provider, 38 | types: { 39 | // Register custom type 40 | Metalog: { 41 | did: "Vec", 42 | unique_name: "Vec" 43 | }, 44 | }, 45 | }); 46 | 47 | // For console testing 48 | window.substrateApi = api; 49 | } 50 | 51 | window.onload = substrate(); 52 | ``` 53 | This function automatically connects to our runtime, registers our custom type, as well as makes the API calls available inside the console of your browser. To test this, you can now start your blockchain runtime and use webpack to generate the main.js file with: 54 | ``` 55 | npm run build 56 | ``` 57 | Next, you can open the index.html file in your browser and open the developer console. At the top, you should see “connect…” and, if you type for example "substrateApi.tx.", you should now be able to select the different modules of your runtime (see the image below). 58 | 59 | 60 | 61 | Now, we'll integrate a function to create a Metalog entry on our blockchain and we'll add an event listener to the button element: 62 | ``` 63 | document.getElementById("button").addEventListener("click", async function () { 64 | let did = document.getElementById("did").value; 65 | let unique_name = document.getElementById("name").value; 66 | 67 | const keyring = new Keyring({ type: 'sr25519' }); 68 | const alice = keyring.addFromUri('//Alice'); 69 | 70 | const transfer = await api.tx.template.createMetalog(did, unique_name); 71 | const hash = await transfer.signAndSend(alice); 72 | console.log('Transfer sent with hash', hash); 73 | }); 74 | ``` 75 | At the beginning of the function, we'll read the content of the two input fields. Next, we'll generate the keys of our Alice user. Of course, usually, you wouldn’t hard-code the user into your application. This is just for testing. Finally, we'll create the transaction, sign it, and send it. 76 | 77 | 78 | 79 | You can easily test if everything works by sending the same transaction twice. In this case you should see an error message inside your runtime terminal. 80 | 81 | **-> [Next: 8. Next Steps](./8_next_steps.md)** 82 | -------------------------------------------------------------------------------- /tutorial/8_next_steps.md: -------------------------------------------------------------------------------- 1 | # 8. Next Steps 2 | 3 | This tutorial will only give you a first introduction into Substrate and to what you can do with it. The following resources will help you to get a better understanding of the technology: 4 | 5 | **Tutorials:** 6 | 7 | * [Substrate Developer Hub](https://substrate-developer-hub.github.io/) 8 | * [How To Build Your Own Blockchain Using Parity Substrate 9 | ](https://hackernoon.com/build-your-blockchain-with-parity-substrate-a8ddc4872ed7) 10 | 11 | **Substrate Github Projects** 12 | * [Joystream Substrate Runtime](https://github.com/Joystream/substrate-runtime-joystream) 13 | * [Parity Shasper](https://github.com/stakedtechnologies/Plasm) 14 | * [Plasm](https://github.com/paritytech/shasper) 15 | * [Starlog](https://github.com/PACTCare/Starlog) 16 | * [Zerochain](https://github.com/LayerXcom/zero-chain) 17 | 18 | 19 | -------------------------------------------------------------------------------- /tutorial/images/interface_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/interface_1.png -------------------------------------------------------------------------------- /tutorial/images/interface_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/interface_2.png -------------------------------------------------------------------------------- /tutorial/images/interface_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/interface_3.png -------------------------------------------------------------------------------- /tutorial/images/setup_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/setup_1.png -------------------------------------------------------------------------------- /tutorial/images/testing_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/testing_1.png -------------------------------------------------------------------------------- /tutorial/images/testing_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/testing_2.png -------------------------------------------------------------------------------- /tutorial/images/testing_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/testing_3.png -------------------------------------------------------------------------------- /tutorial/images/testing_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/testing_4.png -------------------------------------------------------------------------------- /tutorial/images/tests_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/tests_1.png -------------------------------------------------------------------------------- /tutorial/images/ui_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/ui_1.png -------------------------------------------------------------------------------- /tutorial/images/ui_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PACTCare/Substrate-Web3-DNS-Tutorial/f8e2999b55b997f13000bb703b619262bfa3b4c6/tutorial/images/ui_2.png -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ -------------------------------------------------------------------------------- /ui/css/style.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Montserrat); 2 | 3 | html { 4 | background:#ECF0F1; 5 | font-family: 'Montserrat', sans-serif; 6 | } 7 | 8 | a { 9 | display:block; 10 | text-decoration:none; 11 | color:#A2A5A6; 12 | text-align:center; 13 | padding:20px 0 0; 14 | } 15 | 16 | form { 17 | margin:5% auto; 18 | width:400px; 19 | min-height:400px; 20 | background:white; 21 | padding:2.5% 5% 2.5%; 22 | border-radius:2.5%; 23 | } 24 | 25 | form img { 26 | text-align:center; 27 | width:35%; 28 | margin:0 auto; 29 | display:block; 30 | padding:0; 31 | } 32 | 33 | h2 { 34 | font-size:2em; 35 | padding:0; 36 | margin:0; 37 | text-align:center; 38 | } 39 | 40 | input { 41 | margin:5% 0; 42 | border:none; 43 | width:100%; 44 | font-size:1.5em; 45 | padding:0 0 2%; 46 | background:none; 47 | } 48 | 49 | textarea:focus, input:focus{ 50 | outline: 0; 51 | } 52 | 53 | #did { 54 | border-bottom:2px solid #52d9e5; 55 | } 56 | 57 | #name { 58 | border-bottom:2px solid #2a99ef; 59 | } 60 | 61 | label { 62 | color:#BDC3C7; 63 | text-transform:uppercase; 64 | font-size:0.8em; 65 | letter-spacing:4px; 66 | } 67 | 68 | #button { 69 | background:none; 70 | text-align: center; 71 | cursor: pointer; 72 | border:none; 73 | width:100%; 74 | min-height:50px; 75 | margin:10px 0 10px; 76 | border-radius:2.5%; 77 | color:white; 78 | padding: 10px 0 0 0; 79 | font-size:1.75em; 80 | 81 | -webkit-transition: background 1s ease-out; 82 | -moz-transition: background 1s ease-out; 83 | -o-transition: background 1s ease-out; 84 | transition: background 1s ease-out; 85 | 86 | 87 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#2a99ef+0,fbd75c+100 */ 88 | background: #2a99ef; /* Old browsers */ 89 | background: -moz-linear-gradient(left, #2a99ef 0%, #52d9e5 100%); /* FF3.6+ */ 90 | background: -webkit-gradient(linear, left top, right top, color-stop(0%,#2a99ef), color-stop(100%,#52d9e5)); /* Chrome,Safari4+ */ 91 | background: -webkit-linear-gradient(left, #2a99ef 0%,#52d9e5 100%); /* Chrome10+,Safari5.1+ */ 92 | background: -o-linear-gradient(left, #2a99ef 0%,#52d9e5 100%); /* Opera 11.10+ */ 93 | background: -ms-linear-gradient(left, #2a99ef 0%,#52d9e5 100%); /* IE10+ */ 94 | background: linear-gradient(to right, #2a99ef 0%,#52d9e5 100%); /* W3C */ 95 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#2a99ef', endColorstr='#52d9e5',GradientType=1 ); /* IE6-9 */ 96 | } 97 | 98 | #button:hover { 99 | background-position:-400px; 100 | } 101 | 102 | html { 103 | background:rgba(255,255,255,0.95); 104 | } 105 | 106 | -------------------------------------------------------------------------------- /ui/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Substrate 7 | 8 | 9 | 10 | 11 |
12 |

Substrate Tutorial

13 |
14 |
15 | 16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 |
Create Metalog Entry
24 |
25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "webpack" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "webpack": "^4.34.0", 15 | "webpack-cli": "^3.3.4" 16 | }, 17 | "dependencies": { 18 | "@polkadot/api": "0.82.0-beta.9", 19 | "@polkadot/keyring": "0.94.0-beta.3" 20 | } 21 | } -------------------------------------------------------------------------------- /ui/src/index.js: -------------------------------------------------------------------------------- 1 | import { ApiPromise, WsProvider } from '@polkadot/api'; 2 | import { Keyring } from '@polkadot/keyring'; 3 | 4 | const provider = new WsProvider('ws://127.0.0.1:9944'); 5 | 6 | async function substrate() { 7 | console.log('connect...'); 8 | const api = await ApiPromise.create({ 9 | provider: provider, 10 | types: { 11 | // Register custom type 12 | Metalog: { 13 | did: "Vec", 14 | unique_name: "Vec" 15 | }, 16 | }, 17 | }); 18 | 19 | // For console testing 20 | window.substrateApi = api; 21 | 22 | document.getElementById("button").addEventListener("click", async function () { 23 | let did = document.getElementById("did").value; 24 | let unique_name = document.getElementById("name").value; 25 | 26 | const keyring = new Keyring({ type: 'sr25519' }); 27 | const alice = keyring.addFromUri('//Alice'); 28 | 29 | const transfer = await api.tx.template.createMetalog(did, unique_name); 30 | const hash = await transfer.signAndSend(alice); 31 | console.log('Transfer sent with hash', hash); 32 | }); 33 | } 34 | 35 | 36 | 37 | window.onload = substrate(); -------------------------------------------------------------------------------- /ui/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.js', 5 | output: { 6 | filename: 'main.js', 7 | path: path.resolve(__dirname, 'dist') 8 | } 9 | }; --------------------------------------------------------------------------------