DEV Community 👩‍💻👨‍💻

Cover image for Creating a blockchain in 60 lines of Javascript
FreakCdev
FreakCdev

Posted on • Updated on

Creating a blockchain in 60 lines of Javascript

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

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));
    }
}
Enter fullscreen mode Exit fullscreen mode

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 = [];
    }
}
Enter fullscreen mode Exit fullscreen mode

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())];
    }
}
Enter fullscreen mode Exit fullscreen mode

Just for convenience, I'll create a function to get the latest block:

    getLastBlock() {
        return this.chain[this.chain.length - 1];
    }
Enter fullscreen mode Exit fullscreen mode

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));
    }
Enter fullscreen mode Exit fullscreen mode

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;
    }
Enter fullscreen mode Exit fullscreen mode

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();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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));
    }
Enter fullscreen mode Exit fullscreen mode

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 };
Enter fullscreen mode Exit fullscreen mode
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); 
Enter fullscreen mode Exit fullscreen mode

It should look like this:

Image description

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

(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;
    }
Enter fullscreen mode Exit fullscreen mode

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:

GitHub logo nguyenphuminh / JeChain

An experimental, educational purpose proof-of-work 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.

Contacts

Top comments (59)

Collapse
 
jonrandy profile image
Jon Randy • Edited on

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 👍

Collapse
 
alvarezgarcia profile image
Alvarez García

I think that maybe another post showing differences between var, let and const it' s the real deal.

...

Being serious, I wish we have more quality posts like this.

Collapse
 
jonrandy profile image
Jon Randy

Like this one?

Collapse
 
wahidn profile image
WahidN

I totally agree! I hate those posts/tutorials that do nothing but use a package and doesnt explain anything

Collapse
 
freakcdev297 profile image
FreakCdev Author

Thanks a lot man!

Collapse
 
rineezlivares profile image
Rineez Ahmed

Please write about Proof of stake

Collapse
 
freakcdev297 profile image
FreakCdev Author

Sure! Follow me to get the notification of the next article!

Collapse
 
jaybit33 profile image
Justin Roseman

Smart contracts would be great

Collapse
 
labzdjee profile image
Gérard Gauthier

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?

Collapse
 
freakcdev297 profile image
FreakCdev Author

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:

function log16(n) {
    return Math.log(n) / Math.log(16);
}
Enter fullscreen mode Exit fullscreen mode

So the new mine method should look like this:

    mine(difficulty) {
        while (!this.hash.startsWith(Array(Math.round(log16(difficulty)) + 1).join("0"))) {
            this.nonce++;
            this.hash = this.getHash();
        }
    }
Enter fullscreen mode Exit fullscreen mode

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").

Collapse
 
labzdjee profile image
Gérard Gauthier

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.

Collapse
 
tqbit profile image
tq-bit

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 and proof-of-stake, perhaps also the more upgraded variations you mention.

Collapse
 
tqbit profile image
tq-bit

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.

Collapse
 
freakcdev297 profile image
FreakCdev Author

Of course you can! The code is 100% open-source and the whole point of this article is sharing knowledge!

Collapse
 
tawsbob profile image
Dellean Santos

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".

Collapse
 
freakcdev297 profile image
FreakCdev Author

indeed!

Collapse
 
tomavelev profile image
Toma • Edited on

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.

Collapse
 
jacmkno profile image
jacmkno

"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!

Collapse
 
freakcdev297 profile image
FreakCdev Author • Edited on

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

Collapse
 
annajmcdougall profile image
Anna J McDougall

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!

Collapse
 
raymclee profile image
Collapse
 
freakcdev297 profile image
FreakCdev Author

Thanks for commenting this, I have learnt a lot of Simply Explained!

Collapse
 
luizc91 profile image
LuizC91 • Edited on

Is there a possibility of you teach us how to do it in another language?
Before I forget: thank you for the post. It will help me a lot.

Collapse
 
freakcdev297 profile image
FreakCdev Author

I picked Javascript because it's pretty widely used and easy. I might write some variations of this in different languages and upload it to the JeChain repository on Github. In the mean time, I think you can try to write your own by following the concepts mentioned in the article :D

Collapse
 
luizc91 profile image
LuizC91

I will do.

Collapse
 
smaranh profile image
Smaran Harihar

Please continue the series.

Collapse
 
freakcdev297 profile image
FreakCdev Author

Sure man!

Collapse
 
aidenybai profile image
Aiden Bai

cool stuff!

Collapse
 
freakcdev297 profile image
FreakCdev Author

cool!

Collapse
 
opauloh profile image
Paulo Henrique

Very nice article!

Collapse
 
coderduck profile image
duccanhole

good post! from vietnam with love

🌚 Life is too short to browse without dark mode