Substrate Framework: Building Blockchains with Polkadot
Learn to build custom blockchains using Substrate, the framework powering Polkadot.
Substrate Framework: Building Blockchains with Polkadot
Substrate is a blockchain framework written in Rust that enables building custom blockchains.
What is Substrate?
Substrate provides:
- Modular Architecture: Mix and match components
- Runtime Logic: Write in Rust, compile to WebAssembly
- Consensus: Built-in or custom consensus mechanisms
- Networking: P2P networking out of the box
- Forkless Upgrades: Upgrade without hard forks
Key Concepts
Runtime
The runtime contains the business logic of your blockchain. It's compiled to WebAssembly for execution.
RUST// Substrate runtime structure #[frame_support::pallet] pub mod pallet_example { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config { type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>; } #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet<T>(_); #[pallet::storage] pub type Value<T: Config> = StorageValue<_, u32>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { ValueStored(u32, T::AccountId), } #[pallet::call] impl<T: Config> Pallet<T> { #[pallet::weight(10_000)] pub fn set_value(origin: OriginFor<T>, value: u32) -> DispatchResult { let who = ensure_signed(origin)?; Value::<T>::put(value); Self::deposit_event(Event::ValueStored(value, who)); Ok(()) } } }
Pallets
Pallets are modules that provide specific functionality:
pallet-balances: Token balancespallet-timestamp: Time trackingpallet-staking: Staking functionalitypallet-democracy: Governance
Extrinsics
Extrinsics are transactions or calls that modify state.
Events
Events are emitted when state changes occur.
Building a Custom Chain
1. Install Substrate
BASHcurl https://getsubstrate.io -sSf | bash -s -- --fast
2. Create a Node
BASHsubstrate-node-new my-blockchain alice
3. Create a Runtime Module
BASHsubstrate-module-new my-pallet
Key Features
Forkless Upgrades
Substrate enables runtime upgrades without hard forks:
RUST// Upgrade runtime via governance pub fn set_code(new_code: Vec<u8>) -> DispatchResult { // Code upgrade logic }
Custom Consensus
Choose from:
- Aura: Block authoring
- BABE: Block production
- GRANDPA: Finality gadget
- Custom consensus
Interoperability
Substrate chains can connect to Polkadot/Parachains:
- Parachains: Connected to relay chain
- Bridges: Connect to other chains
Real-World Examples
- Polkadot: Relay chain built with Substrate
- Kusama: Canary network
- Moonbeam: Ethereum-compatible parachain
- Acala: DeFi parachain
Advantages
- Fast Development: Pre-built components
- Security: Battle-tested code
- Flexibility: Customize as needed
- Interoperability: Connect to Polkadot ecosystem
- Upgradeability: No hard forks needed
Code Examples
Simple Substrate Pallet
Basic pallet structure
// Simplified Substrate pallet concept
use std::collections::HashMap;
struct Pallet {
storage: HashMap<String, u64>,
}
impl Pallet {
fn new() -> Self {
Pallet {
storage: HashMap::new(),
}
}
fn set_value(&mut self, key: String, value: u64) -> Result<(), String> {
self.storage.insert(key, value);
Ok(())
}
fn get_value(&self, key: &str) -> Option<u64> {
self.storage.get(key).copied()
}
fn emit_event(&self, event: &str) {
println!("Event: {}", event);
}
}
fn main() {
let mut pallet = Pallet::new();
// Set value (extrinsic)
pallet.set_value(String::from("balance"), 1000).unwrap();
// Emit event
pallet.emit_event("ValueStored");
// Get value
if let Some(value) = pallet.get_value("balance") {
println!("Balance: {}", value);
}
}Explanation:
Substrate pallets are modules that provide blockchain functionality. They have storage (state), extrinsics (transactions), and events (notifications). This simplified example shows the basic structure.
Runtime Upgrade Concept
Conceptual runtime upgrade mechanism
// Simplified runtime upgrade concept
struct Runtime {
version: u32,
code: Vec<u8>,
}
impl Runtime {
fn new() -> Self {
Runtime {
version: 1,
code: vec![1, 2, 3, 4],
}
}
fn upgrade(&mut self, new_code: Vec<u8>) -> Result<(), String> {
// In Substrate: validate new code, set new runtime
self.code = new_code;
self.version += 1;
println!("Runtime upgraded to version {}", self.version);
Ok(())
}
fn execute(&self, call: &str) -> Result<String, String> {
// Execute runtime logic
Ok(format!("Executed: {}", call))
}
}
fn main() {
let mut runtime = Runtime::new();
println!("Current version: {}", runtime.version);
// Upgrade runtime (forkless!)
runtime.upgrade(vec![5, 6, 7, 8]).unwrap();
// Execute with new runtime
println!("{}", runtime.execute("set_value").unwrap());
}Explanation:
Substrate enables forkless upgrades by storing runtime code on-chain. The runtime can be upgraded via governance without requiring a hard fork. This is a key advantage of Substrate.
Exercises
Simple Storage Pallet
Create a simple storage pallet!
Starter Code:
use std::collections::HashMap;
struct Pallet {
storage: HashMap<String, u64>,
}
fn main() {
// Create pallet
// Store a value
// Retrieve the value
}