Understanding Blockchain via Code Part 2 of 2

Understanding Blockchain via Code Part 2 of 2


 

Recently I wrote a tutorial to help you understand more about blockchain. Instead of being a merely theoretical, I combined theory and practice with JavaScript to once and for all make sense of everything we've heard about decentralization, blockchain,  and proof of work, etc.

Or rather, I started to make sense, right?

Because several issues were not even touched upon, which I'll try to do in this part!

How do Nodes Validate the Blockchain?


We understand what a block looks like and how blockchain works in general but at one point I called our implementation “naive”, right? Why would it be a naive implementation? That's because as it stands today, it trusts the use they make of it. It trusts that the information will not be tampered with, and below is a very simple tampering to do.

const blockchain = new Blockchain();
blockchain.addBlock([{ from: 'a', to: 'b', amount: 10 }]);
console.log(blockchain);
 
blockchain.blocks[1].data = { from: 'a', to: 'b', amount: 1000 };
console.log(blockchain);

In this example, I'm manually changing the content of a transaction to make the blockchain record report that it transferred 100 times more resources to a wallet.

You might think that a solution would be to hide access to the blocks' internal data or something like that, but remember that blockchains are decentralized and all nodes have the project sources in their hands. In this scenario, we have no way to prevent malicious changes but we can check if they were made!

Remember that each block has a digital signature called a hash? The hash is generated next to the block and if any internal data in the block is changed, the hash will no longer match.

Ah Talha but in this case, isn't it just a matter of having the hash generated again? Yes, but we'll talk more about that later.

For now, let's understand that we need a Blockchain validation mechanism. Something that tells us whether it has been tampered with or not. To do this, we will create a new function in our Blockchain class, called isValid:

isValid() {
    for (let i = this.blocks.length - 1; i > 0; i--) {
        const currentBlock = this.blocks[i];
        const previousBlock = this.blocks[i - 1];
 
        if (currentBlock.hash !== currentBlock.generateHash()
            || currentBlock.previousHash !== previousBlock.hash
            || currentBlock.index !== previousBlock.index + 1) {
            return false
        }
    }
    return true
}

In this implementation, we are validating the blockchain backwards. This is because if an attacker wanted to change a transaction he would most likely choose a more recent one, since the computational effort of recalculating hashes would be smaller in this case and/or it would be easier to deceive the network seeking a consensus for his fraudulent version of the ledger. reason.

Thus, by validating the blockchain backwards we increase the speed with which validation is carried out in fraud scenarios, whereas in common scenarios, the time will always be equal to the total number of elements in the blockchain.

Validation itself has three legs:

  • We validate whether the block hash meets the hash generation rules;
  • We validate whether the previousHash of the block is equal to the hash of the previous block;
  • We validate that the block index is equal to the previous block index +1;

This guarantees the integrity of signatures, references and block order!

If you want to check how it works, you can adjust your index.js as follows:

const Blockchain = require("./Blockchain");
 
const blockchain = new Blockchain();
blockchain.addBlock([{ from: 'a', to: 'b', amount: 10 }]);
blockchain.addBlock([{ from: 'b', to: 'c', amount: 15 }]);
console.log(blockchain);
console.log(blockchain.isValid());
 
blockchain.blocks[1].data = { from: 'a', to: 'b', amount: 1000 };
console.log(blockchain);
console.log(blockchain.isValid())

 

Notice how in the second scenario, in which we tamper  one of the blocks, the blockchain is no longer considered valid, just based on the hashes that now no longer match. However, this is still a naive implementation!

What Prevents Fake Blocks from Being Added to the Blockchain?

Imagine a blockchain like Bitcoin. Because currency has value, have you ever stopped to think about it? Just like gold, it is not obtained for free. There is a price to pay, through hard work. In the case of Bitcoin, hard computational work or more specifically proof of work, the popular “mining”.

Proof of work (PoW) is a computational challenge that every machine that wants to add a new block to the blockchain must solve before it is accepted as valid. It is the  PoW that prevents blocks from simply being mined in as many and as many ways as people want and it also ensures that the network remains active and intact, functioning, because for each challenge solved, the miner who solved it is rewarded with coins from the network itself.

This was the mechanism created by Satoshi Nakamoto to not only reward the electricity expenditure that the network consumes, but for two other reasons: generating scarcity and generating protection against attacks.

The scarcity of the currency would make it have value and was an essential part of its economy. The moment something can be easily obtained, for example adding a block to our current blockchain, you agree that no one would pay to have this block for themselves, right? Why would anyone pay for something that can be generated in just a few milliseconds of processing?

Not only did Satoshi put PoW into the Bitcoin algorithm, he programmed it so that the more miners the network had, the greater the difficulty and as time passed, the lower the rewards were (in a process called halving). These two factors make Bitcoin one of the few deflationary currencies in the world!

Now, the scarcity could backfire: with the appreciation of the currency, it would attract the attention of attackers. However, PoW also helps with this. It guarantees that we would have more people cooperating than attacking the blockchain, since the computational effort for attacks is much greater than for mining. After all, a successful attack would involve recalculating several hashes and propagating the changes to multiple nodes quickly before the blockchain is updated and/or the fraud is noticed.

At this point I imagine you have an abstract understanding of what PoW is. But how do we make this concrete in our code?

We need to create an algorithm that makes it difficult to create a block to the point that it cannot be created without any effort, but also that it is not impossible, including with an adjustable difficulty. The algorithm proposed by Satoshi Nakamoto places a goal on the hash to be generated: it must contain a number of leading zeros. The more zeros, the greater the difficulty in finding the number.

So, based on all the data in the block that we already have, we will add what we call a nonce (number used once) or golden number: a random number that, when concatenated with the other attributes, generates the hash with the correct number of zeros to the left. To reach the target hash, miners will have to test several nonces and only the block will be accepted when the challenge is solved.

Let's modify our Block.js class so that it receives two more attributes and a function:

  • nonce: the golden number that will make the generated hash within the difficulty;
  • difficulty: the number of zeros that the hash must have on the left to be considered valid (aka difficulty);
  • mine: function to perform mining, as there is no point in generating just one hash now;
  • generateHash: we already have this function, but we will have to adjust it to include the nonce;

Translating into code:

const sha256 = require('crypto-js/sha256');
 
module.exports = class Block {
    constructor(index = 0, previousHash = null, data = 'Genesis Block', difficulty = 1) {
        this.index = index;
        this.previousHash = previousHash;
        this.data = data;
        this.timestamp = new Date();
        this.nonce = 0;
 
        const prefix = new Array(difficulty + 1).join("0");
        this.mine(prefix);
    }
 
    mine(prefix) {
        do {
            this.nonce++;
            this.hash = this.generateHash();
        }
        while (!this.hash.startsWith(prefix));
    }
 
    generateHash() {
        return sha256(this.index + this.previousHash + JSON.stringify(this.data) + this.timestamp + this.nonce).toString();
    }
}

 

Note that the adjustment in the constructor includes the nonce, the construction of a string with the prefix of leading zeros and the call to the mining function, instead of simply having the hash generated.

Then we have the mining function itself, which will increment the nonce (another alternative would be to generate it randomly) and use it to generate the hash, which we test with the zero prefix (difficulty).

And finally, we adjust the generateHash function so that it includes the nonce in the generation, which also ends up adjusting the blockchain validation.

Now, you can optionally adjust the Blockchain code so that the difficulty is adjustable and passed to the blocks, or simply test adding blocks and you will see that in addition to being slower (proportional to the difficulty) you will see the nonce numbers in the result.

Blockchain {
blocks: [
1,
Block {
},
index: 0,
previousHash: null,
data: 'Genesis Block',
timestamp: 2023-11-21T22:02:54.820Z,
nonce: 17,
hash: '0b8638ef3519efc71efebdf416da4fe4c4fb579db676c8cf00f6afda8a7f84c5'
Block {
},
index: 1,
previousHash: '0b8638ef3519efc71efebdf416da4fe4c4fb579db676c8cf00f6afda8a7f84c5', data: [Array],
timestamp: 2023-11-21T22:02:54.826Z,
nonce: 3,
hash: '0e920025236eeb67023b2acb9f67dc61fe51b782513eb0972bcad0f1c446cb88'
Block {
}
index: 2,
previousHash: '0e920025236eeb67023b2acb9f67dc61fe51b782513eb0972bcad0f1c446cb88', data: [Array],
timestamp: 2023-11-21T22:02:54.827Z,
nonce: 31,
hash: 0081db5fe5ad446cf3eec6d8d480fbb50bfbea5fe6b103c5ad18355c35b83fc0'
nextIndex: 3
}

Note that with difficulty at 1, with little mining (aka tested nonces) we can already reach the hash that meets the needs. Here on my machine, from difficulty 4 it started taking a few seconds to mine two blocks. And In yours?

That's all for today, in the later post I will dicuss how to program in Solidity, the language used in Smart Contracts, how to create different trading bot's and much more. So, stay with me and don't forget to like and follow!

 

 

How do you rate this article?

8



Blockchain Development
Blockchain Development

A blog that covers everything that's happening in crypto world.

Publish0x

Send a $0.01 microtip in crypto to the author, and earn yourself as you read!

20% to author / 80% to me.
We pay the tips from our rewards pool.