In this tutorial, you will learn to create a custom "Proof of Existence" dApp using the Substrate blockchain development framework and FRAME runtime libraries.
This tutorial should take you about 1 hour to complete. We will be using the Rust programming language and ReactJS, but you do not need to know these to be able to complete this guide. We will provide you with working code snippets and explain what all the code does at a high level.
We only expect that:
- You are generally familiar with software development, writing code, and running your code.
- You have completed the Create Your First Substrate Chain Tutorial.
- You are open to learning about the bleeding edge of blockchain development.
If you run into an issue on this tutorial, we are here to help! You can ask a question on Stack Overflow and use the
substratetag or contact us on Element.
Before we even get started, let's lay out what we are going to do over the course of this tutorial. We will:
- Launch a Substrate blockchain based on a template project.
- Modify this template project to add our own custom logic.
- Modify a front-end template to interact with your brand new blockchain.
Sound reasonable? Good, then let's begin!
You should already have version
v3.0.0of the Substrate Developer Hub Node Template compiled on your computer from when you completed the Create Your First Substrate Chain Tutorial. If you do not, please complete that tutorial.
The Create Your First Substrate Chain Tutorial used the front-end template, so there is no additional set-up required if you have already completed that tutorial.
The dApp we will build is a Proof of Existence (PoE) service. From Wikipedia:
Rather than uploading the entire file to the blockchain to "prove its existence", users submit a hash of the file, known as a file digest or checksum. These hashes are powerful because huge files can be uniquely represented by a small hash value, which is efficient for storing on the blockchain. Any user with the original file can prove that this file matches the one on the blockchain by simply recomputing the hash of the file and comparing it with the hash stored on chain.
To add to this, blockchains also provide a robust identity system. So when a file digest is stored on the blockchain, we can also record which user uploaded that digest. This allows that user to later prove that they were the original person to claim the file.
Our PoE API will expose two callable functions:
create_claim- allows a user to claim the existence of a file by uploading a file digest.
revoke_claim- allows the current owner of a claim to revoke their ownership.
In order to implement this, we will only need to store information about the proofs that have been claimed, and who made those claims.
Sounds simple enough? Great, let's get coding.
The Substrate Developer Hub Node Template, which is used as the starting point for this tutorial, has a FRAME-based runtime. FRAME is a library of code that allows you to build a Substrate runtime by composing modules called "pallets". You can think of these pallets as individual pieces of logic that define what your blockchain can do! Substrate provides you with a number of pre-built pallets for use in FRAME-based runtimes.
For example, FRAME includes a Balances pallet that controls the underlying currency of your blockchain by managing the balance of all the accounts in your system.
If you want to add smart contract functionality to your blockchain, you simply need to include the Contracts pallet.
Even things like on-chain governance can be added to your blockchain by including pallets like Democracy, Elections, and Collective.
The goal of this tutorial is to teach you how to create your own FRAME pallet to include in your custom blockchain! The Substrate Developer Hub Node Template comes with a template pallet that you will use as a starting point to build custom runtime logic on top of.
Open the Node Template in your favorite code editor, then open the file
You will see some pre-written code that acts as a template for a new pallet. You can read over this file if you'd like, and then delete the contents since we will start from scratch for full transparency. When writing your own pallets in the future, you will likely find the scaffolding in this template pallet useful.
At a high level, a FRAME pallet can be broken down into six sections:
Things like events, storage, and callable functions may look familiar to you if you have done other blockchain development. We will show you what each of these components looks like for a basic Proof Of Existence pallet.
Since imports are pretty boring, you can start by pasting this at the top of your empty
Most of these imports are already available because they were used in the template pallet whose code we just deleted. However,
sp_stdis not available and we need to list it as a dependency.
Add this block to your
Then, Update the existing
[features]block to look like this. The last line is new. You will learn more about why this is necessary in the next tutorial, the Add a Pallet tutorial.
Every pallet has a component called
Configthat is used for configuration. This component is a Rust "trait"; traits in Rust are similar to interfaces in languages such as C++, Java and Go. For now, the only thing we will configure about our pallet is that it will emit some Events. The
Configinterface is another topic that will be covered in greater depth in the next tutorial, the Add a Pallet tutorial. The following code will need to be added to your
After we've configured our pallet to emit events, let's go ahead and define which events:
Our pallet will only emit an event in two circumstances:
- When a new proof is added to the blockchain.
- When a proof is removed.
The events can contain some additional data, in this case, each event will also display who triggered the event (
AccountId), and the proof data (as
Vec<u8>) that is being stored or removed. Note that convention is to include an array with descriptive names for these parameters at the end of event documentation.
The events we defined previously indicate when calls to the pallet have completed successfully. Similarly, errors indicate when a call has failed, and why it has failed.
The first of these errors can occur when attempting to claim a new proof. Of course a user cannot claim a proof that has already been claimed. The latter two can occur when attempting to revoke a proof.
To add a new proof to the blockchain, we will simply store that proof in our pallet's storage. To store that value, we will create a hash map from the proof to the owner of that proof and the block number the proof was made.
If a proof has an owner and a block number, then we know that it has been claimed! Otherwise, the proof is available to be claimed.
As implied by our pallet's events and errors, we will have two "dispatchable functions" the user can call in this FRAME pallet:
create_claim(): Allow a user to claim the existence of a file with a proof.
revoke_claim(): Allow the owner of a claim to revoke their claim.
After you've copied all of the parts of this pallet correctly into your
pallets/template/lib.rsfile, you should be able to recompile your node without warning or error. Run this command in the root directory of the
substrate-node-templaterepository to build and run the node:
Now it is time to interact with our new Proof of Existence pallet!
If you have made it this far, that means you should have a brand new blockchain with custom functionality up and running.
We will give you a custom React component that you can use to interact with your node.
To start the Front-End Template, navigate to its directory and run:
yarn installwill install dependencies if this is the first time you run front-end template.
yarn startwill start the template.
In the Front-End Template project, edit the
TemplateModule.jsfile in the
Delete the entire contents of that file, and replace it with the following:
We won't walk you step by step through the creation of this component, but do look over the code comments to learn what each part is doing.
Your Front-End Template should reload when you save your changes, and you'll notice our new component. Now we're ready to try out our new dApp. Select any file on your computer, and you will see that you can create a claim with its file digest:
If you press "Create Claim", a transaction will be dispatched to your custom Proof of Existence pallet, where this digest and the selected user account will be stored on chain.
If all went well, you should see a new
ClaimCreatedevent appear in the Events component. The front-end automatically recognizes that your file is now claimed, and even gives you the option to revoke the claim if you want.
Remember, only the owner can revoke the claim! If you select another user account at the top, and you will see that the revoke option is disabled!
This is the end of our journey into creating a Proof of Existence blockchain.
You have seen firsthand how simple it can be to develop a brand new pallet and launch a custom blockchain using Substrate and FRAME. Furthermore, we have shown you that the Substrate ecosystem provides you with the tools to quickly create responsive front-end experiences so users can interact with your blockchain.
This tutorial chose to omit some of the specific details around development in order to keep this experience short and satisfying. However, we want you to keep learning!
To learn more about building your own pallets, explore the Substrate Recipes.
Complete the Add a Pallet tutorial to learn how to extend the Node Template with additional capabilities from Substrate's set of core FRAME pallets.
Substrate is written in the Rust programming language, which has a great community and many helpful resources. If you would like to learn more about this powerful and beloved programming language, check out the information-packed Rust Book and the fun Rustlings course.
If you experienced any issues with this tutorial or want to provide feedback, You can ask a question on Stack Overflow and use the
substratetag or contact us on Element.
We can't wait to see what you build next!