Building a Custom Blockchain Application with Substrate

Getting Started with Substrate

In this article, we will implement a basic custom blockchain application in Rust using Substrate. Our application will be a simple blogging platform where users can submit blog posts, comment on others’ posts, and send tips to the authors.

Setup

To set up our node template, we’ll use Kickstart, a CLI tool for setting up projects based on premade templates. We’ll use the Substrate template to create a fully wired-up pallet. Run the following command to set up the project:

kickstart new --template substrate-node-template blogchain

This will prompt you for a name; enter “blogchain”. The template will be downloaded and set up with a fully wired-up pallet.

File Structure

Let’s take a look at the file structure of our project:

blogchain/
pallets/
blogchain/
lib.rs
runtime/
src/
lib.rs
...
node/
...
...

We’ll focus on modifying the pallets/blogchain/lib.rs and runtime/src/lib.rs files.

Customizing the Pallet

First, let’s change the pallet name to “blogchain” in runtime/src/lib.rs. We’ll also add the Currency trait to our pallet to enable currency transfers.

Data Structures and Storage

Next, we’ll define data structures for blog posts and comments in pallets/blogchain/lib.rs. We’ll use Substrate’s scale_info macro to derive helper traits for serialization and deserialization.

// Define data structures for blog posts and comments
#[derive(Encode, Decode, Clone, PartialEq, Eq)]
pub struct BlogPost {
    pub id: u64,
    pub content: Vec<u8>,
}

#[derive(Encode, Decode, Clone, PartialEq, Eq)]
pub struct Comment {
    pub id: u64,
    pub post_id: u64,
    pub content: Vec<u8>,
}

We’ll also define two StorageMap instances to hold the blog posts and comments.

// Define StorageMap instances for blog posts and comments
pub type BlogPosts<T> = StorageMap<'BlogPost', Blake2_128Concat, u64, BlogPost<T>>;
pub type Comments<T> = StorageMap<'Comment', Blake2_128Concat, u64, Comment<T>>;

Events

We’ll add events for each of our actions: creating a blog post, commenting on a post, and sending a tip. These events will inform clients and users of changes to the blockchain state.

// Define events for creating a blog post, commenting on a post, and sending a tip
#[derive(Encode, Decode, Clone, PartialEq, Eq)]
pub enum Event {
    CreatedBlogPost { id: u64 },
    CommentedOnPost { post_id: u64, comment_id: u64 },
    SentTip { post_id: u64, amount: u64 },
}

Error Handling

We’ll add error types to handle cases where the blog post or comment is too short or too long. We’ll also define constants for the maximum and minimum lengths of blog posts and comments.

// Define error types for handling invalid blog posts and comments
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
    BlogPostTooShort,
    BlogPostTooLong,
    CommentTooShort,
    CommentTooLong,
}

// Define constants for maximum and minimum lengths of blog posts and comments
pub const MIN_BLOG_POST_LENGTH: u64 = 10;
pub const MAX_BLOG_POST_LENGTH: u64 = 1024;
pub const MIN_COMMENT_LENGTH: u64 = 5;
pub const MAX_COMMENT_LENGTH: u64 = 256;

Functions and Extrinsic

Now, let’s implement our extrinsic functions for creating a blog post, commenting on a post, and sending a tip. We’ll use the pallet::weight macro to define the computational weight of each extrinsic.

// Implement extrinsic functions for creating a blog post, commenting on a post, and sending a tip
#[pallet::call]
impl<T: Config> Pallet<T> {
    #[pallet::weight(10_000)]
    pub fn create_blog_post(origin: OriginFor<T>, content: Vec<u8>) -> DispatchResult {
        // implementation
    }

    #[pallet::weight(5_000)]
    pub fn comment_on_post(origin: OriginFor<T>, post_id: u64, content: Vec<u8>) -> DispatchResult {
        // implementation
    }

    #[pallet::weight(1_000)]
    pub fn send_tip(origin: OriginFor<T>, post_id: u64, amount: u64) -> DispatchResult {
        // implementation
    }
}

Testing

Finally, let’s test our application using the Substrate Front End Template. We’ll build our application for release and start a local node in development mode. Then, we’ll use the Pallet Interactor to create a blog post, comment on it, and send a tip to the author.

  • Build the application for release: cargo build --release
  • Start a local node in development mode: node --dev
  • Use the Pallet Interactor to interact with the node:
// Create a blog post
pallet_interactor create_blog_post "Hello, world!"

// Comment on the post
pallet_interactor comment_on_post 0 "Great post!"

// Send a tip to the author
pallet_interactor send_tip 0 10

Leave a Reply