In recent years, cryptocurrencies and blockchains are two uprising fields, so today, I will share my way of creating a blockchain in Javascript in just 60 lines of codes.
There's also my full tutorial on Youtube. You can check it out for more details.
Also, my new article is released, check it out! It's about creating transactions for our blockchain, basically the first step of building a cryptocurrency.
If two of those are already too familiar for you, consider checking out the third article about how to create a p2p network and release your cryptocurrency. Consider checking it out!
What is a blockchain?
Before we do any coding, we need to understand what a blockchain is. Technically, a blockchain at its bare minimum is just a list containing objects that have some basic information on it like timestamp, transactions, hash,... Its data must be immutable and unhackable. Modern platforms like Ethereum, Cardano, Polkadot,... have way more complex stuff, but we are staying simple in this article.
Setup
We are using Node.js for this project, so be sure to install it if you haven't.
Throughout the article, I will be using the object-oriented programming style, so I expect you to know basic knowledge about it.
Creating a block
As I have said, a block is just an object that has some information on it, so we should have a Block
class like this:
class Block {
constructor(timestamp = "", data = []) {
this.timestamp = timestamp;
// this.data should contain information like transactions.
this.data = data;
}
}
So we have our timestamp
and data
, but a blockchain needs immutability. We can gain that effect by using a hashing function that hashes all of our properties in the block. I suggest reading about hasing on wikipedia, it plays an essential role in a blockchain. Basically, it takes in a message and outputs a "hashed" one with fixed length, a slight change to the message will make the output completely different.
I'm using the sha256
algorithm. To implement its hashing function, I'll just going to use the Nodejs' built-in crypto
package:
const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");
The code above should give us what we wanted, but if you want to know how it works, check out Node.js's official doc about the hash class.
We should have something like this:
// Get the sha256 hash function.
const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");
class Block {
constructor(timestamp = "", data = []) {
this.timestamp = timestamp;
this.data = data;
this.hash = this.getHash();
this.prevHash = ""; // previous block's hash
}
// Our hash function.
getHash() {
return SHA256(this.prevHash + this.timestamp + JSON.stringify(this.data));
}
}
Because every time anything is changed, SHA256 will throw out something completely different, so that can some what ensures the immutability.
The prevHash
property also plays a big role in immutability, it ensures that the blocks will stay unchanged along the blockchain's lifespan. It contains the hash of the previous block, so you can assure the immutability of that previous block since a slight change will make the current block's getHash
be different. You can see that it's empty, but we will do something with it later in this article.
The blockchain
Let's move over to the blockchain class.
Like I have said, a blockchain is a list with blocks, so we can have a basic form like this:
class Blockchain {
constructor() {
// This property will contain all the blocks.
this.chain = [];
}
}
You must have a genesis block, which is technically just the first block:
class Blockchain {
constructor() {
// Create our genesis block
this.chain = [new Block(Date.now().toString())];
}
}
Just for convenience, I'll create a function to get the latest block:
getLastBlock() {
return this.chain[this.chain.length - 1];
}
Now, we should have a way to add a block to the blockchain.
addBlock(block) {
// Since we are adding a new block, prevHash will be the hash of the old latest block
block.prevHash = this.getLastBlock().hash;
// Since now prevHash has a value, we must reset the block's hash
block.hash = block.getHash();
// Object.freeze ensures immutability in our code
this.chain.push(Object.freeze(block));
}
Validation
We need to know whether the chain is still valid or not, so we need a method to check validation. The chain is valid if a block's hash is equal to what its hashing method returns, and a block's prevHash
property should be equal to the previous block's hash.
isValid(blockchain = this) {
// Iterate over the chain, we need to set i to 1 because there are nothing before the genesis block, so we start at the second block.
for (let i = 1; i < blockchain.chain.length; i++) {
const currentBlock = blockchain.chain[i];
const prevBlock = blockchain.chain[i-1];
// Check validation
if (currentBlock.hash !== currentBlock.getHash() || prevBlock.hash !== currentBlock.prevHash) {
return false;
}
}
return true;
}
This method will play a really important role when our blockchain is ran on a p2p network.
Proof-of-work
In a peer-to-peer network, where there are no 3rd party system to approve people's action, without any consensus mechanism, nodes (people to be simple) will agree with the majority, but people can start being attackers and take control of the majority, so we need a consensus mechanism. A consensus mechanism exist not entirely to stop attacks, they exist to make people not be attackers. Proof-of-work is one of them.
Before we go more on to that, the system works by making you increase a value called nonce to get the hash which starts with a number of zeros equals/relates to the difficulty.
PoW can help with 2 things: It prevents attackers because it's near impossible to catch up with other nodes alone, and it provides mining rewards so people would try to be neutral rather than being attackers. We will implement mining rewards in the next article when we have a transaction system.
We can implement the PoW system by adding a mine
method and a nonce
property to our block:
class Block {
constructor(timestamp = "", data = []) {
this.timestamp = timestamp;
this.data = data;
this.hash = this.getHash();
this.prevHash = ""; // previous block's hash
this.nonce = 0;
}
// Our hash function.
getHash() {
return SHA256(this.prevHash + this.timestamp + JSON.stringify(this.data) + this.nonce);
}
mine(difficulty) {
// Basically, it loops until our hash starts with
// the string 0...000 with length of <difficulty>.
while (!this.hash.startsWith(Array(difficulty + 1).join("0"))) {
// We increases our nonce so that we can get a whole different hash.
this.nonce++;
// Update our new hash with the new nonce value.
this.hash = this.getHash();
}
}
}
Because when we change a small detail in our block, the hash will be completely different, so we are just incrementing nonce over and over again until the hash matches the one we need.
(Note that Bitcoin and others normally use a different way to check difficulty, but we are staying simple)
Moving over to the Blockchain
class, we should create a difficulty property:
this.difficulty = 1;
I will set it to 1, the difficulty should update based on how many blocks mined.
We must update the addBlock
method from the Blockchain too:
addBlock(block) {
block.prevHash = this.getLastBlock().hash;
block.hash = block.getHash();
block.mine(this.difficulty);
this.chain.push(Object.freeze(block));
}
Now, all blocks need to be mined before being added to the chain.
Quick note
Because we are staying simple, so I used the proof-of-work system for this blockchain. Note that most modern blockchains use a way better system called proof-of-stake (or many of its upgraded variations).
Testing out the chain!
Create a new file, that file will be the entry file.
Let's use our freshly created blockchain! I'll call it JeChain
for now.
Export the needed classes first:
module.exports = { Block, Blockchain };
const { Block, Blockchain } = require("./your-blockchain-file.js");
const JeChain = new Blockchain();
// Add a new block
JeChain.addBlock(new Block(Date.now().toString(), { from: "John", to: "Bob", amount: 100 }));
// (This is just a fun example, real cryptocurrencies often have some more steps to implement).
// Prints out the updated chain
console.log(JeChain.chain);
It should look like this:
The first block is our genesis block, the second block is the added block.
Updated bonus: Difficulty and block time
Block time
Block time is a constant value that resembles estimated time for a block to be added to the chain. Platforms like Bitcoin has block time of 10 minutes, while Ethereum has block time of 13 seconds.
Bitcoin's difficulty formula
With Bitcoin, its difficulty is updated every 2016 blocks were mined. It uses this formula to calculate its new difficulty:
old difficulty * (2016 blocks * 10 minutes) / mining time for the previous 2016 blocks
Now, let's code!
First, we must have our block time first, I'll just set it to 30 seconds, which is equal to 30000ms. I'm using millisecond because it works better with Date.now()
.
this.blockTime = 30000;
(Note that we are coding in the Blockchain
class here).
Just as an example, I'll create my own system: the difficulty will be incremented by 1 if block time is less than the actual time the block's mined, it will be decremented otherwise.
addBlock(block) {
block.prevHash = this.getLastBlock().hash;
block.hash = block.getHash();
block.mine(this.difficulty);
this.chain.push(Object.freeze(block));
this.difficulty += Date.now() - parseInt(this.getLastBlock().timestamp) < this.blockTime ? 1 : -1;
}
Important note!!!
Because of how we checked difficulty earlier, this should work fine. However, it is better to check difficulty using log16(difficulty)
rather than the difficulty itself, and by doing that, you can now use the Bitcoin's difficulty formula.
You can come up with your own formula though. You should consider what is the best for security while still having good performance.
Source code
You can get the full source code at this repo:
nguyenphuminh / JeChain
JeChain decentralized application platform and smart contract blockchain network
Honorable mention
I have learnt a lot about blockchains from Simply Explained. This article might never exist without help from their videos. Please check them out on Youtube, they have really good blockchain tutorial series.
I also grabbed some info on this article too. Check them out!
Off-topic
Should I continue the series? And if yes, what should I write about? Proof-of-stake? Full cryptocurrency? Smart contracts? Please let me know in the comment section.
Top comments (58)
Thank you sincerely for writing a post titled like this that doesn't just: include a library, set some parameters, and call a function - those kind of posts are garbage.
Dev.to needs more posts like this - real content that is actually interesting. Please keep up the good work 👍
I think that maybe another post showing differences between
var
,let
andconst
it' s the real deal....
Being serious, I wish we have more quality posts like this.
Like this one?
I totally agree! I hate those posts/tutorials that do nothing but use a package and doesnt explain anything
Thanks a lot man!
Please write about Proof of stake
Sure! Follow me to get the notification of the next article!
Smart contracts would be great
Very interesting. I was with you until your mentioning log16(difficulty): where do you use this log16 function? As a parameter in the mine function? Elsewhere?
What I meant was log base 16 of the difficulty, it is a better since the "real difficulty" should not drop or increase by a huge margin, and Bitcoin also uses it.
You can create a simple
log16
function like this:So the new
mine
method should look like this:Also, for a little bonus, Bitcoin also requires 8 zeros by default so if you want to make it Bitcoin-like, it should be
"00000000"+Array(Math.round(log16(difficulty)) + 1).join("0")
.Thank you! That's is very clear. Effectively not as a parameter in the mine function as I mistakenly mentioned it but included in the calculation itself. It looks like the proof of work starts quite harsh with 8 zeroes and evolves quite slowly because of log16. Except maybe if one more "0" adds a lot of more difficulty in the computational demand.
I dare to say this post is one of the best I've read on dev.to.
I'd love to read more about
proof-of-work
andproof-of-stake
, perhaps also the more upgraded variations you mention.PS: I'd like to add that I used your post and replicated its functionality in typescript. It's longer than 60 lines, but I would like to publish it to a Public Github Repos.
Would you grant me the permission to do so? I'd add a reference to your article and to your github repos as well.
Of course you can! The code is 100% open-source and the whole point of this article is sharing knowledge!
I think the most complex is to make the consensus of the network, the way of mining (proof of work for example) the block acceptance rules and the entire ecosystem, the blockchain itself is just a "database".
indeed!
youtu.be/zVqczFZr124?list=RDCMUCnx...
youtu.be/HneatE69814?list=RDCMUCnx...
Thanks for commenting this, I have learnt a lot of Simply Explained!
Good post for understanding the basics. The only thing left is some simple synchronisation with a network and the solution of the Byzantine Generals Problem offered by Satoshi.
For me (with effectively zero blockchain knowledge but advanced JavaScript), I was with you until the 'nonce' and 'mine' part. I think I'll have to check out the YouTube you linked to get the full story there! Regardless, this did help clarify a few of the basics for me, so thank you!
"Catching up to others" after changing the chain is impossible without the need to compute "nonce" anyway, right? Since the hash always include the previous hash, that does ensure the order is preserved too, so what, is the point of "nonce", except just for supporting the mining dificulty? Is that it's only purpose?
Great work! thanks for taking the time to craft this pure-value post!
In a peer to peer network: If you don't have the nonce value, you can change data and then change the hash to what getHash returns, same with prevHash. You can do that with all following blocks and still get a valid chain. You can do that with thousands of nodes and manipulate the chain. But with PoW, it's not dependent on the majority of nodes, it depends on the length of the chain. If you submit a faulty chain, and you don't have enough computational power, your chain will likely be shorter and shorter than the chain from normal miners as time progresses, so your chain will be invalid. And technically, because of rewards, people would like to contribute to the chain more than attacking it.
Please correct me if I'm wrong, I'm learning as I'm writing :D