Using the concepts of JavaScript to create Bitcoin

The best way to learn about something is to attempt to build it from the ground up to see what happens. This blog is all about how to use JavaScript classes and Node.js to build a simple cryptocurrency named Bitcoin that has taken the world by storm

We’ll go over the fundamentals of Bitcoin in an easy-to-understand manner:

  • The blockchain is a network of interconnected blocks.
  • The block reward system, as well as the block reward schedule.
  • The complexity of the blocks, as well as the improvements to the difficulty of the blocks.
  • The pending transaction mempool.
  • A completed block’s structure.
  • New transactions and blocks are communicated across the network.

To do this, we decided to vastly simplify everything:

  • It wouldn’t be production-ready.
  • In every way, it will be different from Bitcoin.
  • It wouldn’t be secure in any way.
  • It wouldn’t be especially performant.

An image is worth a thousand words in this case:

Bitcoin

With laser eyes, Bitcoin is the Mona Lisa. That thing on the right is what we are working on.

Fundamental Principles of Bitcoin

Before we get started, it’s a good idea to brush up on some Bitcoin fundamentals.

  • A blockchain is what gives Bitcoin its strength.
  • A ‘block’ is a type of data object that is propagated across the Bitcoin network so that everybody has the same state and source of reality, including the same transactions.
  • Each block is cryptographically linked to the one before it, resulting in a long chain of blocks, each containing new data and transactions.
  • Each block contains transactions, allowing coins to be exchanged between network participants. Bob sends 5 coins to Jane, 3 coins to Harry, and so on. By looking at all of the historical blocks, we can keep track of the balances.
  • To be created, blocks must be ‘mined,’ which requires a certain amount of computational power and luck. This protects the network because it takes more computing resources than anyone else on the network to hack or attack Bitcoin.
  • Anyone who successfully mines a block receives a set number of coins as a reward. Miners are encouraged to continue mining as a result of this.
  • Optional payments are also sent with transactions. Along with the block reward, these payments are earned by the block miner. As a result, there is a double motivation to mine new blocks.
  • Mining blocks becomes incrementally harder or easier over time, ensuring that each new block is mined at a consistent pace. It’s important to maintain a steady block rate: if the block time is too long, the network will become unusable. If the block time is too short, the network can be quickly flooded with competing blocks.
  • All of these rules, as well as the block format, are agreed upon by every node and miner in the network. Even if you have a lot of computing power, this makes it incredibly difficult to build fake blocks or submit transactions that spend the same coins multiple times.

The BlockChain Interface for Bitcoin

To begin, we devised a set of types for the blockchain’s public interface.

export type TransactionType = {|
    sender : string,
    receiver : string,
    amount : number,
    fee : number
|};

export type BlockType = {|
    miner : string,
    parentid : string,
    id : string,
    index : number,
    time : number,
    elapsed : number,
    transactions : Array<TransactionType>,
    difficulty : number,
    reward : number
|};

The following are the most relevant factors to consider:

  • Sender, recipient, number, and a user-defined fee are all required for transactions. The fee would decide the transaction’s priority on the network.
  • Blocks, each of which will contain a number of vital areas.

The following items will be included in each block:

  • A unique identifier for the current block.
  • To identify who gets the reward for each block, a miner id (public key) is used.
  • To enable the blocks to form a linear chain, there is a parent block id.
  • An index to make it easier to keep track of the blocks.
  • A timestamp indicates when the block was mined.
  • The interval between this block and the last is represented by an elapsed time.
  • A list of transfers that can be used to trace the sending and receiving of coins.
  • To hold the block time constant, there is a complexity mode.
  • To encourage miners, there is a block incentive.

Creating a set of rules for Bitcoin

Next, we'll describe some parameters that will govern how the blockchain operates.
export const BLOCK_TIME = 1000;

export const INITIAL_REWARD = 1024;

export const REWARD_HALVING_SCHEDULE = 20;

export const BLOCK_SIZE_LIMIT = 10;

export const GENESIS_BLOCK : BlockType = {
    id:           'GENESIS',
    parentid:     'GENESIS',
    miner:        'SATOSHI',
    index:        1,
    time:         now(),
    elapsed:      0,
    transactions: [],
    difficulty:   1,
    reward:       INITIAL_REWARD
};

We’ve got some crucial information here:

  • BLOCK_TIME is the amount of time we want to wait between each block generation. Bitcoin strives for a 10-minute gap between blocks; we’ll go for 1 second so we can test it locally.
  • FOR EACH BLOCK, INITIAL_REWARD is the coin reward. This reward is given to the person who mined the block. This reward would gradually decrease, encouraging more miners to try to mine blocks, process transactions, and keep the network safe and free of double-spends and other attacks.
  • REWARD_HAVING_SCHEDULE is the number of blocks before this reward is divided in half. Every four years, the value of Bitcoin is halved. Every twenty blocks, or every 20 seconds, will be our goal.
  • The maximum number of transactions that can be included in a block is defined by BLOCK_SIZE_LIMIT. We’ll set it to ten, but the actual cap in Bitcoin is far higher because it’s based on the block’s MB scale.
  • The initial hard-coded block is specified by GENESIS_BLOCK. This genesis block would be the father of all other blocks. For the time being, we’ll give this block to SATOSHI and set a default difficulty and initial block reward.

When we use them in our code, the significance of each of these should hopefully become clearer.

Trying to figure out the bitcoin

Let's stub out the blockchain next. For all blocks and transactions, this will be our "database" or "system-of-record."
export type BlockChainType = {|
    addBlock : (hashedBlock : string) => Promise<void>,
    createBlock : (publicKey : string, transactions : Array<string>) => Promise<?string>,
    getBalances : () => Promise<Counter>
|};

export function BlockChain() : BlockChainType {
    const createBlock = async (publicKey, transactions) : Promise<?string> => {

    };
    
    const addBlock = async (block : BlockType) => {

    };
    
    const getBalances = async () : Promise<Counter> => {

    };

    return {
        addBlock,
        createBlock,
        getBalances
    };
}

Now we can start putting some of these roles into effect!

Defining the data structure of the blockchain for bitcoin

First, there's the blockchain's data structure:
const root = TreeNode(GENESIS_BLOCK);

You might think of a blockchain, such as Bitcoin, as a linked list, with each block acting as a node.

A blockchain is really something like a tree of bricks.

Bitcoin

This tree may have a lot of competing branches. How can we tell which branch to believe? It’s easy! We choose the longest branch because it required the most computational effort to develop. The longer the branch, the more certain we can be that it will not be overtaken by another branch.

As a result, we represent our blockchain with a tree data structure in memory, which helps us to quickly calculate the longest branch at any given time.

We use GENESIS_BLOCK as the tree’s root since it will always be the first block. This can be hard-coded to make it simpler and has no drawbacks.

Bitcoin Blocks for mining

Let’s now write some code to create a new block. This would be the most difficult workout of the day.

const createBlock = async (publicKey : string, transactions : Array<string>) : Promise<?string> => {
    const headBlock = root.getLongestBranchNode().getValue();

    const newTransactions = await asyncFilter(transactions, verifyPackedSignature);

    const newDifficulty = (headBlock.elapsed) > BLOCK_TIME
        ? headBlock.difficulty - 1
        : headBlock.difficulty + 1;

    const newReward = divisibleBy(headBlock.index, REWARD_HALVING_SCHEDULE)
        ? Math.floor(headBlock.reward / 2)
        : headBlock.reward;

    const newBlock = {
        miner:        publicKey,
        parentid:     headBlock.id,
        index:        headBlock.index + 1,
        id:           uniqueID(),
        time:         now(),
        elapsed:      now() - headBlock.time,
        transactions: newTransactions,
        difficulty:   newDifficulty,
        reward:       newReward
    };

    const hashedBlock = await hashAndPack(newBlock);
    const hash = unpackHash(hashedBlock);

    if (divisibleBy(hash, headBlock.difficulty)) {
        return hashedBlock;
    }
};

To begin mining a block, we'll need three things:
  1. The existing blockchain depicted as the root in this diagram. To find the current ‘true’ blockchain, which we want to mine another block on top of, we must use getLongestBranchNode().
  2. The miner’s publicKey is a string that contains the miner’s publicKey. This must be included in the block in order for the rest of the network to know who will receive the reward for mining the block.
  3. A transactional list. If we don’t have any transactions to publish to the network, mining a block is pointless.

The majority of this is easy. In the new block, we include the miner’s public key, the id, the parent’s id, the time, the index, and the transactions.

We’ll re-validate the transactions here to ensure they’re signed correctly. Before accepting the new block, every other node will do the same, and we want to increase our chances of it being approved without giving anyone a reason to reject it.

Dealing with the payout is the next tricky part.

Every 20 blocks, we want to halve the incentive. So, for the current block index, we calculate the current reward, assuming the reward begins at 1024 and halves every 20 blocks. Checking if the current block index is divisible by 20, and halving the reward if it is, is an easy way to achieve that. Otherwise, we’ll continue to receive the same payout.

The next step is to determine the block’s new complexity.

In fact, Bitcoin’s difficulty is recalculated every 2016 block or roughly every 2 weeks. For the sake of convenience, we’ll recalculate the complexity for each new block so that we can demonstrate this in real-time.

This is a very naive method of determining complexity, but it works in this case.

  • We reduce the complexity by one if the block took too long to produce.
  • We increase the complexity by 1 if the block took too little time to produce.

This means we’re constantly adjusting how difficult it is to make the next block based on how long it took us to make the previous block. This ensures that no matter how many miners are attempting to mine new blocks, blocks are formed at a roughly constant pace.

The higher the ‘hash rate,’ which is an indicator of how much computing power is currently being used to mine new coins around the world, the more miners you have.

Then you either have:

  • There are a limited number of miners, and the hash rate is low, so the difficulty is low.
  • There are a lot of miners, and the hash rate is good, but the complexity is high.

This means that Hash-Rate / Difficulty should always be a constant number, resulting in a relatively constant average time for each new block.

We’re still not finished once we’ve made a new block. The network can accept the block only if the complexity is right, as determined by the previous block.

We’re going to test this difficulty in a fairly naive way right now. We hash the block, convert it to a number, and allow it to pass if the number is equally divisible by the difficulty with no remainder.

This means that as the difficulty number rises, the chances of the block passing the difficulty check decrease, and we’ll have to make more and more guesses to create a new block. This necessitates more computing power and time.

Likewise, the inverse is real. If the difficulty number decreases, there is a better probability that a new block will pass the difficulty check, and generating a new block will take fewer guesses. This requires less computing power and time.

Since a number is more likely to be divisible by a low number like 2 than by a higher number like 17, this simple version of a complexity check works. As a result, generating a block with a hash that is divisible by 2 is simpler, while generating a block with a hash that is divisible by 17 is more difficult.

Adding bitcoins chain of blocks

We need to be able to connect new blocks to our tree data structure as they are propagated across the network.
const addBlock = async (hashedBlock : string) => {
    const verifiedBlock = await verifyHashAndUnpack(hashedBlock);
    const verifiedTransactions = await asyncMap(verifiedBlock.transactions, verifySignatureAndUnpack);

    const fullyVerifiedBlock = {
        ...verifiedBlock,
        transactions: verifiedTransactions
    };

    const headNode = root.findValueByID(fullyVerifiedBlock.parentid);
    const hash = unpackHash(hashedBlock);

    if (headNode && divisibleBy(hash, headNode.getValue().difficulty)) {
        headNode.addChildNodeValue(fullyVerifiedBlock);
    }
};

First, we’ll check that the block matches its hash and that all of the transactions inside it are properly signed.

Then we’ll check to see if the block followed any of the rules. For the time being, we’re only looking at the difficulty matches. In practise, we’d want to check each and every field of the block to make sure it’s right. Since we can’t trust any other node on the network, we need to double-check all.

Balances are calculated for bitcoin wallets

The final step is to create a system that can handle any of the following:

  • transactions.
  • Charges.
  • Reward Blocks.
Then, using the values in each block, calculate the final balances for each wallet on the chain.
const getBalances = async () : Promise<Counter> => {
    const balances = new Counter();

    for (let { miner, reward, transactions } of root.getLongestChainAsValues()) {
        balances.add(miner, reward);

        for (let { receiver, amount, fee, sender } of transactions) {
            balances.add(miner, fee);
            balances.add(receiver, amount);
            balances.subtract(sender, amount);
            balances.subtract(sender, fee);
        }
    }

    return balances;
};

This is done by calculating the length of the longest chain at any given time and adding up all of the balances, fees, and block rewards. Each block contains all of the information we need to complete this task.

  • For each block, the miner receives a block reward.
  • The fee for each transaction goes to the miner.
  • The sum of the transaction is given to the recipient.
  • The sender loses the transaction’s value.
  • The transaction fee is forfeited by the sender.

The blockchain is now complete!

We’ve completed the data-structure/database portion of the project. We have a blockchain, and we can build new blocks, add them to the tree, and use the longest chain of blocks in the tree to measure the final balance of each wallet.

But we’re not done yet!

Bitcoin isn’t limited to only one machine or server. We’ll need a way to link to a peer-to-peer network, exchange these blocks with others, and publish new transactions. As a result, we’ll need a server!

Using the Node for building bitcoin

We'll need to identify some types and stubs for our Node once more so that we can code against them.
type BitcoinNodeType = {|
    mine : () => Promise<void>,
    send : (receiver : string, amount : number, fee : number) => Promise<void>
|};

export function BitcoinNode() : BitcoinNodeType 

    const mine = async () : Promise<void> => {

    };

    const send = async (receiver : string, amount : number, fee : number) : Promise<void> => {

    };

    return {
        mine,
        send
    };
}

Credentials, Blockchain, and Network Access for bitcoin

First, we'll create the resources we'll need to run the node:
const keypair = KeyPair();
const network = Network();
const blockchain = BlockChain();
let mempool = [];

We are keeping an ear out for any new transactions.

We'll need to be able to listen for transactions sent from any other network nodes:
network.listen('ADD_TRANSACTION', async (signedTransaction) => {
    mempool.push(signedTransaction);     
});

A new transaction is being sent for bitcoin

We'd like to be able to submit our own new transactions, of course:
const send = async (receiver : string, amount : number, fee : number) : Promise<void> => {
    const signedTransaction = await signAndPack({
        sender: await keypair.publicKey,
        receiver,
        amount,
        fee
    }, await keypair.publicKey, await keypair.privateKey)

    await network.broadcast('ADD_TRANSACTION', signedTransaction);
};

We are keeping an ear out for new blocks.

Any node on the network can build blocks, and this is where most blocks would come from in the real world. As a result, we must be able to listen for new blocks from others and incorporate them into our blockchain.
network.listen('ADD_BLOCK', async (hashedBlock) => {
    mempool = [];
    await blockchain.addBlock(hashedBlock);
});

The node is up and running, and new blocks are being mined.

Finally, we'd like to try running the node and mining new blocks:
const mine = async () : Promise<void> => {
    await loop(async () => {
        const transactions = mempool.slice(0, BLOCK_SIZE_LIMIT);
        const hashedBlock = await blockchain.createBlock(await keypair.publicKey, transactions);

        if (hashedBlock) {
            await network.broadcast('ADD_BLOCK', hashedBlock);
        }
    });
};

If we are successful in mining a new block, we publish it to the network so that other nodes can consume it.

If someone else mines a new block before us, we restart and try to mine a new block on top of that one instead, because createBlock always tries to mine on top of the longest chain of blocks at any given time.

Create Bitcoin using JavaScript

Blockchain Explained!

Stay Connected!

Are you looking for a JavaScript developer?

Book your FREE call with our technical consultant now.
Let's Build Your App

Book your FREE call with our technical consultant now.

Totally enjoyed working with Karan and his team on this project. They brought my project to life from just an idea. Already working with them on a second app development project.

They come highly recommended by me.

Martins
OwnerOwner, Digital Babies