diff --git a/substrate/bin/node-template/README.md b/substrate/bin/node-template/README.md index a5929d21f3563b658fe449a0a964124967c29ec7..ad514617ee3254e082177d4bf7ed69dbd3d621c2 100644 --- a/substrate/bin/node-template/README.md +++ b/substrate/bin/node-template/README.md @@ -8,8 +8,8 @@ Follow these steps to prepare a local Substrate development environment :hammer_ ### Simple Setup -Install all the required dependencies with a single command (be patient, this can take up -to 30 minutes). +Install all the required dependencies with a single command (be patient, this can take up to 30 +minutes). ```bash curl https://getsubstrate.io -sSf | bash -s -- --fast @@ -17,7 +17,8 @@ curl https://getsubstrate.io -sSf | bash -s -- --fast ### Manual Setup -Find manual setup instructions at the [Substrate Developer Hub](https://substrate.dev/docs/en/knowledgebase/getting-started/#manual-installation). +Find manual setup instructions at the +[Substrate Developer Hub](https://substrate.dev/docs/en/knowledgebase/getting-started/#manual-installation). ### Build @@ -54,8 +55,8 @@ RUST_LOG=debug RUST_BACKTRACE=1 ./target/release/node-template -lruntime=debug - ### Multi-Node Local Testnet To see the multi-node consensus algorithm in action, run a local testnet with two validator nodes, -Alice and Bob, that have been [configured](/bin/node-template/node/src/chain_spec.rs) as the -initial authorities of the `local` testnet chain and endowed with testnet units. +Alice and Bob, that have been [configured](/bin/node-template/node/src/chain_spec.rs) as the initial +authorities of the `local` testnet chain and endowed with testnet units. Note: this will require two terminal sessions (one for each node). @@ -92,6 +93,91 @@ cargo run -- \ Execute `cargo run -- --help` to learn more about the template node's CLI options. +## Template Structure + +A Substrate project such as this consists of a number of components that are spread across a few +directories. + +### Node + +A blockchain node is an application that allows users to participate in a blockchain network. +Substrate-based blockchain nodes expose a number of capabilities: + +- Networking: Substrate nodes use the [`libp2p`](https://libp2p.io/) networking stack to allow the + nodes in the network to communicate with one another. +- Consensus: Blockchains must have a way to come to + [consensus](https://substrate.dev/docs/en/knowledgebase/advanced/consensus) on the state of the + network. Substrate makes it possible to supply custom consensus engines and also ships with + several consensus mechanisms that have been built on top of Web3 Foundation research. +- RPC Server: A remote procedure call (RPC) server is used to interact with Substrate nodes. + +There are several files in the `node` directory - take special note of the following: + +- [`chain_spec.rs`](./node/src/chain_spec.rs): A + [chain specification](https://substrate.dev/docs/en/knowledgebase/integrate/chain-spec) is a + source code file that defines a Substrate chain's initial (genesis) state. Chain specifications + are useful for development and testing, and critical when architecting the launch of a + production chain. Take note of the `development_config` and `testnet_genesis` functions, which + are used to define the genesis state for the local development chain configuration. These + functions identify some + [well-known accounts](https://substrate.dev/docs/en/knowledgebase/integrate/subkey#well-known-keys) + and use them to configure the blockchain's initial state. +- [`service.rs`](./node/src/service.rs): This file defines the node implementation. Take note of + the libraries that this file imports and the names of the functions it invokes. In particular, + there are references to consensus-related topics, such as the + [longest chain rule](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#longest-chain-rule), + the [Aura](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#aura) block authoring + mechanism and the + [GRANDPA](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#grandpa) finality + gadget. + +After the node has been [built](#build), refer to the embedded documentation to learn more about the +capabilities and configuration parameters that it exposes: + +```shell +./target/release/node-template --help +``` + +### Runtime + +The Substrate project in this repository uses the +[FRAME](https://substrate.dev/docs/en/knowledgebase/runtime/frame) framework to construct a +blockchain runtime. FRAME allows runtime developers to declare domain-specific logic in modules +called "pallets". At the heart of FRAME is a helpful +[macro language](https://substrate.dev/docs/en/knowledgebase/runtime/macros) that makes it easy to +create pallets and flexibly compose them to create blockchains that can address a variety of needs. + +Review the [FRAME runtime implementation](./runtime/src/lib.rs) included in this template and note +the following: + +- This file configures several pallets to include in the runtime. Each pallet configuration is + defined by a code block that begins with `impl $PALLET_NAME::Trait for Runtime`. +- The pallets are composed into a single runtime by way of the + [`construct_runtime!`](https://crates.parity.io/frame_support/macro.construct_runtime.html) + macro, which is part of the core + [FRAME Support](https://substrate.dev/docs/en/knowledgebase/runtime/frame#support-library) + library. + +### Pallets + +The runtime in this project is constructed using many FRAME pallets that ship with the +[core Substrate repository](https://github.com/paritytech/substrate/tree/master/frame) and a +template pallet that is [defined in the `pallets`](./pallets/template/src/lib.rs) directory. + +A FRAME pallet is compromised of a number of blockchain primitives: + +- Storage: FRAME defines a rich set of powerful + [storage abstractions](https://substrate.dev/docs/en/knowledgebase/runtime/storage) that makes + it easy to use Substrate's efficient key-value database to manage the evolving state of a + blockchain. +- Dispatchables: FRAME pallets define special types of functions that can be invoked (dispatched) + from outside of the runtime in order to update its state. +- Events: Substrate uses [events](https://substrate.dev/docs/en/knowledgebase/runtime/events) to + notify users of important changes in the runtime. +- Errors: When a dispatchable fails, it returns an error. +- Trait: The `Trait` configuration interface is used to define the types and parameters upon which + a FRAME pallet depends. + ## Generate a Custom Node Template Generate a Substrate node template based on a particular commit by running the following commands: diff --git a/substrate/bin/node-template/node/src/chain_spec.rs b/substrate/bin/node-template/node/src/chain_spec.rs index 3edef7946868520f74dd3190df72e1b0b80cb23e..e49457b0b9a2e4bec7c0da9bb7d5f489a421ca5c 100644 --- a/substrate/bin/node-template/node/src/chain_spec.rs +++ b/substrate/bin/node-template/node/src/chain_spec.rs @@ -8,13 +8,13 @@ use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::traits::{Verify, IdentifyAccount}; use sc_service::ChainType; -// Note this is the URL for the telemetry server -//const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; +// The URL for the telemetry server. +// const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>; -/// Helper function to generate a crypto pair from seed +/// Generate a crypto pair from seed. pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) .expect("static values are valid; qed") @@ -23,14 +23,14 @@ pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Pu type AccountPublic = <Signature as Verify>::Signer; -/// Helper function to generate an account ID from seed +/// Generate an account ID from seed. pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where AccountPublic: From<<TPublic::Pair as Pair>::Public> { AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account() } -/// Helper function to generate an authority key for Aura +/// Generate an Aura authority key. pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { ( get_from_seed::<AuraId>(s), @@ -42,15 +42,20 @@ pub fn development_config() -> Result<ChainSpec, String> { let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?; Ok(ChainSpec::from_genesis( + // Name "Development", + // ID "dev", ChainType::Development, move || testnet_genesis( wasm_binary, + // Initial PoA authorities vec![ authority_keys_from_seed("Alice"), ], + // Sudo account get_account_id_from_seed::<sr25519::Public>("Alice"), + // Pre-funded accounts vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), @@ -59,10 +64,15 @@ pub fn development_config() -> Result<ChainSpec, String> { ], true, ), + // Bootnodes vec![], + // Telemetry None, + // Protocol ID None, + // Properties None, + // Extensions None, )) } @@ -71,16 +81,21 @@ pub fn local_testnet_config() -> Result<ChainSpec, String> { let wasm_binary = WASM_BINARY.ok_or("Development wasm binary not available".to_string())?; Ok(ChainSpec::from_genesis( + // Name "Local Testnet", + // ID "local_testnet", ChainType::Local, move || testnet_genesis( wasm_binary, + // Initial PoA authorities vec![ authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob"), ], + // Sudo account get_account_id_from_seed::<sr25519::Public>("Alice"), + // Pre-funded accounts vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), @@ -97,14 +112,20 @@ pub fn local_testnet_config() -> Result<ChainSpec, String> { ], true, ), + // Bootnodes vec![], + // Telemetry None, + // Protocol ID None, + // Properties None, + // Extensions None, )) } +/// Configure initial storage state for FRAME modules. fn testnet_genesis( wasm_binary: &[u8], initial_authorities: Vec<(AuraId, GrandpaId)>, @@ -114,10 +135,12 @@ fn testnet_genesis( ) -> GenesisConfig { GenesisConfig { system: Some(SystemConfig { + // Add Wasm runtime to storage. code: wasm_binary.to_vec(), changes_trie_config: Default::default(), }), balances: Some(BalancesConfig { + // Configure endowed accounts with initial balance of 1 << 60. balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), }), aura: Some(AuraConfig { @@ -127,6 +150,7 @@ fn testnet_genesis( authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), }), sudo: Some(SudoConfig { + // Assign network admin rights. key: root_key, }), } diff --git a/substrate/bin/node-template/pallets/template/Cargo.toml b/substrate/bin/node-template/pallets/template/Cargo.toml index fd1766c112b3feeb70078d1db93e7fbb74cf0f32..a025a9b929e952905016b3d32f310b10bdbd8374 100644 --- a/substrate/bin/node-template/pallets/template/Cargo.toml +++ b/substrate/bin/node-template/pallets/template/Cargo.toml @@ -6,7 +6,7 @@ version = "2.0.0-rc5" license = "Unlicense" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" -description = "FRAME pallet template" +description = "FRAME pallet template for defining custom runtime logic." [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/substrate/bin/node-template/pallets/template/src/lib.rs b/substrate/bin/node-template/pallets/template/src/lib.rs index efcce785cceb89a350a8acdd9b211ea71f7007e2..729a71278aa9f1c2f37760274b547253799871fb 100644 --- a/substrate/bin/node-template/pallets/template/src/lib.rs +++ b/substrate/bin/node-template/pallets/template/src/lib.rs @@ -1,13 +1,8 @@ #![cfg_attr(not(feature = "std"), no_std)] -/// A FRAME pallet template with necessary imports - -/// Feel free to remove or edit this file as needed. -/// If you change the name of this file, make sure to update its references in runtime/src/lib.rs -/// If you remove this file, you can remove those references - -/// For more guidance on Substrate FRAME, see the example pallet -/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// https://substrate.dev/docs/en/knowledgebase/runtime/frame use frame_support::{decl_module, decl_storage, decl_event, decl_error, dispatch, traits::Get}; use frame_system::ensure_signed; @@ -18,89 +13,87 @@ mod mock; #[cfg(test)] mod tests; -/// The pallet's configuration trait. +/// Configure the pallet by specifying the parameters and types on which it depends. pub trait Trait: frame_system::Trait { - // Add other types and constants required to configure this pallet. - - /// The overarching event type. + /// Because this pallet emits events, it depends on the runtime's definition of an event. type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>; } -// This pallet's storage items. +// The pallet's runtime storage items. +// https://substrate.dev/docs/en/knowledgebase/runtime/storage decl_storage! { - // It is important to update your storage name so that your pallet's - // storage items are isolated from other pallets. + // A unique name is used to ensure that the pallet's storage items are isolated. + // This name may be updated, but each pallet in the runtime must use a unique name. // ---------------------------------vvvvvvvvvvvvvv trait Store for Module<T: Trait> as TemplateModule { - // Just a dummy storage item. - // Here we are declaring a StorageValue, `Something` as a Option<u32> - // `get(fn something)` is the default getter which returns either the stored `u32` or `None` if nothing stored + // Learn more about declaring storage items: + // https://substrate.dev/docs/en/knowledgebase/runtime/storage#declaring-storage-items Something get(fn something): Option<u32>; } } -// The pallet's events +// Pallets use events to inform users when important changes are made. +// https://substrate.dev/docs/en/knowledgebase/runtime/events decl_event!( pub enum Event<T> where AccountId = <T as frame_system::Trait>::AccountId { - /// Just a dummy event. - /// Event `Something` is declared with a parameter of the type `u32` and `AccountId` - /// To emit this event, we call the deposit function, from our runtime functions - /// [something, who] + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] SomethingStored(u32, AccountId), } ); -// The pallet's errors +// Errors inform users that something went wrong. decl_error! { pub enum Error for Module<T: Trait> { - /// Value was None + /// Error names should be descriptive. NoneValue, - /// Value reached maximum and cannot be incremented further + /// Errors should have helpful documentation associated with them. StorageOverflow, } } -// The pallet's dispatchable functions. +// Dispatchable functions allows users to interact with the pallet and invoke state changes. +// These functions materialize as "extrinsics", which are often compared to transactions. +// Dispatchable functions must be annotated with a weight and must return a DispatchResult. decl_module! { - /// The module declaration. pub struct Module<T: Trait> for enum Call where origin: T::Origin { - // Initializing errors - // this includes information about your errors in the node's metadata. - // it is needed only if you are using errors in your pallet + // Errors must be initialized if they are used by the pallet. type Error = Error<T>; - // Initializing events - // this is needed only if you are using events in your pallet + // Events must be initialized if they are used by the pallet. fn deposit_event() = default; - /// Just a dummy entry point. - /// function that can be called by the external world as an extrinsics call - /// takes a parameter of the type `AccountId`, stores it, and emits an event + /// An example dispatchable that takes a singles value as a parameter, writes the value to + /// storage and emits an event. This function must be dispatched by a signed extrinsic. #[weight = 10_000 + T::DbWeight::get().writes(1)] pub fn do_something(origin, something: u32) -> dispatch::DispatchResult { - // Check it was signed and get the signer. See also: ensure_root and ensure_none + // Check that the extrinsic was signed and get the signer. + // This function will return an error if the extrinsic is not signed. + // https://substrate.dev/docs/en/knowledgebase/runtime/origin let who = ensure_signed(origin)?; - // Code to execute when something calls this. - // For example: the following line stores the passed in u32 in the storage + // Update storage. Something::put(something); - // Here we are raising the Something event + // Emit an event. Self::deposit_event(RawEvent::SomethingStored(something, who)); + // Return a successful DispatchResult Ok(()) } - /// Another dummy entry point. - /// takes no parameters, attempts to increment storage value, and possibly throws an error + /// An example dispatchable that may throw a custom error. #[weight = 10_000 + T::DbWeight::get().reads_writes(1,1)] pub fn cause_error(origin) -> dispatch::DispatchResult { - // Check it was signed and get the signer. See also: ensure_root and ensure_none let _who = ensure_signed(origin)?; + // Read a value from storage. match Something::get() { + // Return an error if the value has not been set. None => Err(Error::<T>::NoneValue)?, Some(old) => { + // Increment the value read from storage; will error in the event of overflow. let new = old.checked_add(1).ok_or(Error::<T>::StorageOverflow)?; + // Update the value in storage with the incremented result. Something::put(new); Ok(()) }, diff --git a/substrate/bin/node-template/pallets/template/src/mock.rs b/substrate/bin/node-template/pallets/template/src/mock.rs index 130a782bb7ba21f20d36e3a5c476da27842f2446..8c3bf2b40473c81b2fa946b3fae86d250f448c3f 100644 --- a/substrate/bin/node-template/pallets/template/src/mock.rs +++ b/substrate/bin/node-template/pallets/template/src/mock.rs @@ -1,5 +1,3 @@ -// Creating mock runtime here - use crate::{Module, Trait}; use sp_core::H256; use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; @@ -12,9 +10,8 @@ impl_outer_origin! { pub enum Origin for Test {} } -// For testing the pallet, we construct most of a mock runtime. This means -// first constructing a configuration type (`Test`) which `impl`s each of the -// configuration traits of pallets we want to use. +// Configure a mock runtime to test the pallet. + #[derive(Clone, Eq, PartialEq)] pub struct Test; parameter_types! { @@ -23,6 +20,7 @@ parameter_types! { pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); } + impl system::Trait for Test { type BaseCallFilter = (); type Origin = Origin; @@ -50,13 +48,14 @@ impl system::Trait for Test { type OnKilledAccount = (); type SystemWeightInfo = (); } + impl Trait for Test { type Event = (); } + pub type TemplateModule = Module<Test>; -// This function basically just builds a genesis storage key/value store according to -// our desired mockup. +// Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { system::GenesisConfig::default().build_storage::<Test>().unwrap().into() } diff --git a/substrate/bin/node-template/pallets/template/src/tests.rs b/substrate/bin/node-template/pallets/template/src/tests.rs index ec123a50c7cc984f15c8f80983ed300d95ecdccf..3356b29ff35980d08d751b4eca01aac403df8380 100644 --- a/substrate/bin/node-template/pallets/template/src/tests.rs +++ b/substrate/bin/node-template/pallets/template/src/tests.rs @@ -1,15 +1,12 @@ -// Tests to be written here - use crate::{Error, mock::*}; use frame_support::{assert_ok, assert_noop}; #[test] fn it_works_for_default_value() { new_test_ext().execute_with(|| { - // Just a dummy test for the dummy function `do_something` - // calling the `do_something` function with a value 42 + // Dispatch a signed extrinsic. assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); - // asserting that the stored value is equal to what we stored + // Read pallet storage and assert an expected result. assert_eq!(TemplateModule::something(), Some(42)); }); } @@ -17,7 +14,7 @@ fn it_works_for_default_value() { #[test] fn correct_error_for_none_value() { new_test_ext().execute_with(|| { - // Ensure the correct error is thrown on None value + // Ensure the expected error is thrown when no value is present. assert_noop!( TemplateModule::cause_error(Origin::signed(1)), Error::<Test>::NoneValue diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/substrate/bin/node-template/runtime/src/lib.rs index 04acf8fa7a904d7ab39e7b4f75d889a2dea578c2..c46d515a3efdc9797c514ea08c8e193197b2fa09 100644 --- a/substrate/bin/node-template/runtime/src/lib.rs +++ b/substrate/bin/node-template/runtime/src/lib.rs @@ -1,5 +1,3 @@ -//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. - #![cfg_attr(not(feature = "std"), no_std)] // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. #![recursion_limit="256"] @@ -40,7 +38,7 @@ pub use frame_support::{ }, }; -/// Importing a template pallet +/// Import the template pallet. pub use template; /// An index to a block. @@ -93,7 +91,6 @@ pub mod opaque { } } -/// This runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node-template"), impl_name: create_runtime_str!("node-template"), @@ -108,7 +105,7 @@ pub const MILLISECS_PER_BLOCK: u64 = 6000; pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; -// These time units are defined in number of blocks. +// Time is measured by number of blocks. pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; @@ -134,6 +131,8 @@ parameter_types! { pub const Version: RuntimeVersion = VERSION; } +// Configure FRAME pallets to include in runtime. + impl system::Trait for Runtime { /// The basic call filter to use in dispatchable. type BaseCallFilter = (); @@ -258,11 +257,12 @@ impl sudo::Trait for Runtime { type Call = Call; } -/// Used for the module template in `./template.rs` +/// Configure the pallet template in pallets/template. impl template::Trait for Runtime { type Event = Event; } +// Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where Block = Block, @@ -277,7 +277,7 @@ construct_runtime!( Balances: balances::{Module, Call, Storage, Config<T>, Event<T>}, TransactionPayment: transaction_payment::{Module, Storage}, Sudo: sudo::{Module, Call, Config<T>, Storage, Event<T>}, - // Used for the module template in `./template.rs` + // Include the custom logic from the template pallet in the runtime. TemplateModule: template::{Module, Call, Storage, Event<T>}, } );