Substrate is one of the fastest way to build blockchains. It enables developers create customised blockchains optimized for any use cases. In this post, we’re going to walk through the steps of building a proof of existence blockchain with Substrate. First, we will go over the basics of substrate, proof-of-existence blockchains, and the prerequisites to build on substrate. Then we will dive deep into our topic for today- creating a PoE blockchain using substrate.
So let’s get started!
What is Substrate?
Substrate is a software development kit (SDK) used for developing customized blockchains. Similar to a web application framework, it is also an open-source framework. The difference between Substrate and web application framework is that Substrate finds application in building decentralized systems like cryptocurrencies or message bus. Applications built on Substrate can work on Polkadot. But, it is not exclusive to Polkadot, as Substrate is used to build Polkadot, and applications built with Substrate can work exclusively elsewhere.
Some of the benefits Substrate offers developers who build with the SDK are:
Efficient Consensus Algorithm
While Substrate is primarily created for cryptocurrency, you can build an efficient project that offers you an efficient consensus algorithm. You get for free, when you build with Substrate, a byzantine fault tolerance. This means even after components of your system fail, the system will continue to work because the Substrate SDK comes equipped with inbuilt fault tolerance.
Open Source SDK
The Substrate SDK and the entire architecture are released through an open-source license. So you have the freedom to build whatever you want. In the past few years that the SDK has been released coupled with the growing popularity of Polkadot, the Substrate community has continued to grow. You can always get the support you need from the substrate and Polkadot community.
Cross Platform
Unlike most blockchain systems in the world of crypto, Substrate works cross-platform. You can even run a file/database abstraction on a browser. With Cross-Consensus Messaging (XCM), Substrate-based blockchains can interconnect with each other.
Future-Proof
Substrate is built to ensure that it is useful in the future. Built inside the SDK is the ability to upgrade the SDK, adapt it based on needs, and clone and integrate code from other decentralized applications. You can make the nodes of your applications alter the runtime depending on the specific condition. This means over time, your project would work seamlessly and adapt to specific conditions without failing at the systemic level.
Why Proof of Existence?
One of the utilities of blockchain is the ability to store any document on the blockchain, which is the ledger publicly distributed. Basically Proof of Existence, as its name suggests, is a mechanism to prove that a digital document exists at a particular time. Document in this case is not limited to popular documents but could be any digital file. A hashing algorithm is used to create an output called a digest. It’s one-way. That means you can’t get back the original input from the output anyway. This makes it secure that once a document has been recorded on the publicly distributed ledger, it is within the realm of impossibility to remove it.
For substrate, either of two hashing algorithms is used. These are twox-hash and blake2 functions implemented in the Rust Language.
The benefit of using a proof of existence is that it does not require a third party to notarize a document. As it is stored in the blockchain alongside its timestamp, claims are not contestable. There are several areas where proof of existence has found success. Georgia is an example where its land registry has been implemented on a blockchain.
What You Need Before Building with Substrate
There are few prerequisites before we delve into the blockchain creation using Substrate. Make sure you have a blue check for all the following:
- You have a shell terminal on your local computer alongside a working knowledge of the command line.
- You have a decent internet connection. This is necessary as some of the tools you need would be downloaded through the internet via the command line.
- You have an understanding of software development. This is necessary as you might run into an error, and you will need to debug.
- You know the core ideas of blockchain and smart contract platforms.
- You have Rust installed on your computer and have your development environment set up already.
- Also install Node.js on your local computer before we jump into the next step.
If you have checked all these, you can get into the tutorial.
How to Create a Proof of Existence with Substrate
The following are the steps you will follow if you want to create a proof of existence with Substrate.
Step 1: Install Substrate
With the Substrate node template, you will be able to build with Substrate. You would have to compile a Substrate node template to continue.
- First, you have to open a terminal shell on your local computer.
- Then you should clone the repository containing the Substrate node template with:
git clone https://github.com/substrate-developer-hub/substrate-node-template
- Change the directory to the root of the node template with the following command:
cd substrate-node-template
- If everything went well, you can compile the Substrate node template with:
cargo build --release
Step 2: Run the Local Node
After the compilation of the node, you should follow the following steps:
- Run the following command to start the node in development mode:
./target/release/node-template --dev
- Ensure that the node is running by reviewing the code generated in the command line.
Step 3: Install the Front-End Template
To interact with the blockchain, Substrate uses a front-end template that is based on ReactJS. The front-end template of Substrate requires Node.js and yarn. You can install yarn with Node.js. Following these steps will help you install the front-end template.
- Install yarn with the following command:
npm install --global yarn
- Clone the front-end template repository to your local computer with the following command:
git clone https://github.com/substrate-developer-hub/substrate-front-end-template
- Change the directory to the root of the front-end template with the following command:
cd substrate-node-template
- If everything went well, you should install the dependencies for the front-end template with the following command:
yarn install
Step 4: Start the Front-End Template
With the above steps done and assuming no errors, you should run the following command to start the front-end template.
- Run the following command to start the front-end template:
yarn start
- Open http://localhost:8000 in your browser to view the front-end template
Step 5: Create Custom Key Chains
The next step is to create custom key chains with an authority set that validates the transactions privately.
You have to purge the unnecessary data on the chain. Then, you have to create the custom keys.
- The following commands can purge the chain of old data:
./target/release/node-template purge-chain --base-path /tmp/alice --chain local
- You will be prompted to confirm the transaction by the command line which will show:
Are you sure to remove "/tmp/alice/chains/local_testnet/db"? [y/N]:
- Confirm the action by entering: y
- You can start the local blockchain node with the alice account when you run the following command:
./target/release/node-template \
--base-path /tmp/alice \
--chain local \
--alice \
--port 30333 \
--ws-port 9945 \
--rpc-port 9933 \
--node-key 0000000000000000000000000000000000000000000000000000000000000001 \
--telemetry-url "wss://telemetry.polkadot.io/submit/ 0" \
--validator
- You can add a second node to the blockchain network by purging the chain of old data, and then running the following command:
./target/release/node-template \
--base-path /tmp/bob \
--chain local \
--bob \
--port 30334 \
--ws-port 9946 \
--rpc-port 9934 \
--telemetry-url "wss://telemetry.polkadot.io/submit/ 0" \
--validator \
--bootnodes/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp
Step 6: Set Up Scaffolding for a Pallet
To be able to build a proof of existence, you need a custom pallet that will behave due to certain conditions with you specifying them. The following steps will help you set up a scaffolding for a custom pallet:
- Change directory with the following command:
cd pallets/template/src
- Run the following command to remove some files:
benchmarking.rs
mock.rs
tests.rs
- With a text editor, open the lib.rs file.
- Delete all the lines in lib.rs file
- Include the macro that is required to build the native Rust Binary (std) and WebAssembly binary
#![cfg_attr(not(feature = "std"), no_std)]
- Build the frame of the pallet by blocking sections that the custom pallet need:
// Re-export pallet items to access from the crate namespace.
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
#[pallet::config] // <-- Part of Step 7. code block will replace this.
#[pallet::event] // <-- Part of Step 7. code block will replace this.
#[pallet::error] // <-- Step 8. code block will replace this.
#[pallet::storage] // <-- Part of Step 9 . code block will replace this.
#[pallet::call] // <-- Part of Step 9. code block will replace this.
}
Step 7: Create Events for the Custom Pallet
In each pallet, there is Config which is a Rust Trait. With this Trait, you can configure the settings for any particular pallet. Then, you have to specify the behavior of the pallet under certain conditions. Follow these to create events for the custom pallet:
- With a file editor, open the pallets/template/src/lib.rs file.
- Replace the #[pallet::config] block with the following code:
/// To Configure the pallet specify the parameters and types on which it depends.
#[pallet::config]
pub trait Config: frame_system::Config {
/// This palette emits events, hence it is dependent on the runtime's definition of an event.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
- In the #[pallet::event] section, replace with the following code:
// Pallets use events to inform users when important changes are made.
// Event documentation should end with an array that provides descriptive names for parameters.
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Event emitted when a claim has been created.
ClaimCreated { who: T::AccountId, claim: T::Hash },
/// Event emitted when a claim is revoked by the owner.
ClaimRevoked { who: T::AccountId, claim: T::Hash },
}
- Save the modifications
Step 8: Define Pallet Errors
It is important you define errors in your pallet. This shows you that a call failed and why it failed. The following will assist you in defining pallet errors:
- With a file editor, open the pallets/template/src/lib.rs file.
- Replace the #[pallet::error] with the following code:
#[pallet::error]
pub enum Error<T> {
/// The claim already exists.
AlreadyClaimed,
/// The claim does not exist, so it cannot be revoked.
NoSuchClaim,
/// The claim is owned by another account, so caller can't revoke it.
NotClaimOwner,
}
- Save the modifications.
Step 9: Implement Storage and Callable Functions
At this point, you are close to the final leg of the proof of existence app. To add a claim to the blockchain, you must implement a storage mechanism. You will also need to create functions that either create a claim or revoke a claim. Follow these:
- With a file editor, open the pallets/template/src/lib.rs file.
- Replace the #[pallet::storage] section with the following code:
#[pallet::storage]
pub(super) type Claims<T: Config> = StorageMap<_, Blake2_128Concat, T::Hash, (T::AccountId, T::BlockNumber)>;
- Replace the #[pallet::call] section with the following code:
// Dispatchable functions allow 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.
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(0)]
pub fn create_claim(origin: OriginFor<T>, claim: T::Hash) -> DispatchResult {
// Check that the extrinsic was signed and get the signer.
// This function will return an error if the extrinsic is not signed.
let sender = ensure_signed(origin)?;
// Verify that the specified claim has not already been stored.
ensure!(!Claims::<T>::contains_key(&claim), Error::<T>::AlreadyClaimed);
// Get the block number from the FRAME System pallet.
let current_block = <frame_system::Pallet<T>>::block_number();
// Store the claim with the sender and block number.
Claims::<T>::insert(&claim, (&sender, current_block));
// Emit an event that the claim was created.
Self::deposit_event(Event::ClaimCreated { who: sender, claim });
Ok(())
}
#[pallet::weight(0)]
pub fn revoke_claim(origin: OriginFor<T>, claim: T::Hash) -> DispatchResult {
// Check that the extrinsic was signed and get the signer.
// This function will return an error if the extrinsic is not signed.
let sender = ensure_signed(origin)?;
// Get owner of the claim, if none return an error.
let (owner, _) = Claims::<T>::get(&claim).ok_or(Error::<T>::NoSuchClaim)?;
// Verify that sender of the current call is the claim owner.
ensure!(sender == owner, Error::<T>::NotClaimOwner);
// Remove claim from storage.
Claims::<T>::remove(&claim);
// Emit an event that the claim was erased.
Self::deposit_event(Event::ClaimRevoked { who: sender, claim });
Ok(())
}
}
- Save the modifications and close the file.
Step 10: Build the Runtime
If the above steps go according to plan, you should be able to compile your Substrate node at this point.
- Ensure that the code compiles by running: cargo check -p node-template-runtime –release
- Go to the root directory of the node template
- Compile the node template by running: cargo build –release
- Begin the node in development mode by running: ./target/release/node-template –dev
- Make sure that the node produces blocks
Your Substrate Proof of Existence app is complete.
How Zeeve Can Help?
This is how we can use substrate to create a proof-of-existence blockchain. But it can be little time and resource consuming to create, deploy and maintain entire DevOps of a blockchain node. As a developer you have to keep yourself updated with the latest protocol upgrades, best practices, key security measures along with a series of Do’s and Don’ts. Zeeve can save 97% of time and 60% of your cost.
Zeeve helps to create a custom blockchain in minutes with substrate. We also offer Polkadot parachain as a service, providing expert support in the backend so you can focus on continuing to grow your business.
Let Zeeve put all the efforts in writing codes, managing and deploying nodes, and you only focus on building. Give us a try and see how Zeeve simplifies your Polkadot and Substrate experience.