Blockchain and Cryptocurrency in TypeScript

This article presents a custom cryptocurrency blockchain I developed in TypeScript, inspired by the excellent Udemy course Build a Blockchain and a Cryptocurrency from Scratch in JavaScript by David Katz. This article deals with the design and theoretical aspects of a blockchain and cryptocurrency, as well as the technical aspects of setting up the development environment, coding and testing. All code, tests and configuration files are available on Github.

The blockchain implements a cryptocurrency application that allows users to transfer funds between blockchain participants. The video below shows how it works.

Features

This cryptocurrency blockchain has many standard features of popular blockchains like Bitcoin and Ethereum. Many of these features are taken from the original Bitcoin whitepaper:

  • peer to peer secure blockchain server that accepts multiple connections through a published REST API
  • autonomous blockchain network with clients that can engage and disengage from the blockchain
  • full blockchain replication among all the clients
  • timestamp on each block so they can be properly ordered
  • mining with a proof of work system for adding new blocks to the blockchain with a dynamic difficulty level and a financial incentive
  • transaction system for transferring funds between nodes
  • secure wallets for storing a public-private key pair
  • digital signatures (SHA-256) and payment verification
  • full suite of unit tests for every aspect of the system

Development Setup

Dev Stack

The block chain was developed with TypeScript (2.9.2), Visual Studio CodeNode.js (10.5), Postman and uses Jest for unit testing.

The following Node modules were used:

  • nodemon for automatic reloading of project on every save (npm i nodemon –save-dev)
  • jest for unit testing (npm i jest –save-dev)
  • @types-jest (npm i @types/jest) for importing Jest symbols into TypeScript unit tests
  • typescript for writing code in TypeScript (npm i -g typescript)
  • tslint (npm i tslint -g)
  • crypto-js (cryptographic package for generating SHA-256 hashes) (npm i crypto-js –save)
  • @types/crypto-js (npm i @types/crypto-js –save) for importing crypto-js symbols into TypeScript
  • express as a HTTP web server (npm i express –save)
  • @types/express for importing express symbols into TypeScript (npm i @types/express –save-dev)
  • body-parser as middleware for parsing HTTP requests and receiving POST requests in JSON format (npm i body-parser –save)
  • ws, a Node.js web socket library (npm i ws –save)
  • @types/ws – type definitions for ws (npm i @types/ws –save)
  • elliptic – for using elliptic curve cryptography to generate public-private keypairs (npm i elliptic –save)
  • uuid – for generating unique transaction ID’s (npm i uuid –save)
  • @types/uuid – type definitions for uuid (npm i @types/uuid –save)

TypeScript Configuration and Issues

TypeScript compiler options were set to be more strict to make the code more type safe. Specifically, the “noImplicitAny” was set to “true” to make the compiler force the use of explicit types throughout the code.

tsconfig.json used for this project:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
    "compilerOptions": {
        "strictNullChecks": true,
        "noImplicitAny": true,
        "moduleResolution": "node",
        "target": "es5",
        "lib": [
            "es2015", "dom"
        ],
        "sourceMap": true
    },
    "include": [
        "src/**/*",
        "test/**/*",
    ],
}

The compiler target in tsconfig.json was set to “es5”. When I tried to set it to “es6” I had many problems running Jest tests because Node does not support es6 imports and I was getting the runtime error: “SyntaxError: Unexpected token import” when importing a module like this:

import { Block } from "../../src/blockchain/block"

So then I tried setting the compile target in tsconfig.json to “es5” but that would result in a compilation error in one of the ws (web sockets) type definition file (web sockets):

node_modules/@types/ws/index.d.ts:181:18 - error TS2304: Cannot find name 'Set'

So I was stuck and tried to find a solution. I looked at using Babel to transpile from ES6 to ES5 as well as using

 node --experimental-modules file.mjs

as outlined in Stack Overflow question: Node.js – SyntaxError: Unexpected token import .

I finally found a more simple solution on this Stack Overflow answer that talks about using the “lib” compiler option in tsconfig.json:

"lib": ["es2015", "dom"]

which referenced the Angular TypeScript documentation that stated:

TypeScript includes a special declaration file called lib.d.ts. This file contains the ambient declarations for various common JavaScript constructs present in JavaScript runtimes and the DOM.

Based on the --target, TypeScript adds additional ambient declarations like Promise if the target is es6.

Since the QuickStart is targeting es5, you can override the list of declaration files to be included:

content_copy
"lib": ["es2015", "dom"]
Thanks to that, you have all the es6 typings even when targeting es5.

 

Unit Testing with Jest

This blockchain was thoroughly unit tested using the Jest testing framework. All Jest unit tests are in the Github test folder and are appended with a “.test” to correspond with the file they are testing. For example, transaction.test.ts contains the unit tests for transaction.ts.

Jest needs to be configured to run in Node mode instead of its default “jsdom” or browser mode. The jest section in package.json, is:


1
2
3
4
5
  "jest": {
    "transform": {},
    "testRegex": "/test/.*\\.test\\.(js)$",
    "testEnvironment": "node"
  }

 

Blockchain – Trustless System

A blockchain is a public and immutable shared ledger that stores all transactions for an application, such as a cryptocurrency like Bitcoin and Ethereum. For a cryptocurrency application, the ledger stores all transactions such as who transferred funds to who, similar to a bank ledger. Each set of transactions is represented as a block on the blockchain. A block can store multiple transactions, as shown in the demo video.

Once a transaction has been committed (mined) to the blockchain (ledger), it can’t be edited or removed. Also, all nodes on the blockchain network keep a replicated copy of the blockchain so there is no central place where the block chain is stored. When a new block is added to the blockchain, all nodes in the blockchain network are updated to have the newest version of the blockchain.

Every block in the chain is linked to the previous block by storing the hash value of the previous block, which creates a chain of blocks.

Instead of a central trusted authority like a bank, the blockchain network itself acts as the trusted authority because of its built in trust and security features. This eliminates the need for middle men such as banks and doesn’t have a single point of failure. An attacker would have to take over a large segment of a blockchain network in order to compromise it whereas a bank robber only needs to rob a single bank to steal a lot of money.

Block Class

The fundamental object of the block chain is the block, which represents an individual link in the blockchain. The blockchain stores the following properties:

  • current hash (based on the timestamp, hash of previous block and transactions)
  • hash of the previous block
  • timestamp
  • data to store (cryptocurrency transactions but can store generic data, as well)
  • nonce value (for mining new blocks)
  • difficulty value (for mining new blocks)

The code for the Block class is in src/blockchain/block.ts. This snippet shows the above properties:


1
2
3
4
5
6
7
8
9
10
export default class Block {
  timestamp: number;
  lastHash: string;
  hash: string;
  //for cryptocurrency - transactions are the data in each block
  //but the blockchain can hold any data
  data: any;
  nonce: number;
  difficulty: number;
}

Genesis Block

The Genesis Block is the very first block in a blockchain – a way to start a blockchain. Since there’s no preceding block, the genesis block uses hard coded dummy values for its previous hash value. This way, when the second block gets added to the blockchain, it will set the value of its previous hash to the hash of the genesis block. Since the genesis block is hard coded, we make it a static method of the Block class, so it’s accessible without instantiating a Block object.

The code for the genesis block is in src/blockchain/block.ts:


1
2
3
4
5
6
7
8
9
  /**
   * First block of the blockchain.
   */

  static getGenesisBlock(): Block {
    let genesisTx: Transaction = new Transaction();
    genesisTx.id = "genesis";
    genesisTx.txInput = new TransactionInput(0, "-----");
    return new Block(0, '-----', 'f1r5t-ha4h', genesisTx, 0, config.DIFFICULTY);
  }

Blockchain Class

The Blockchain class is responsible for

  • storing the list of blocks on the blockchain
  • adding new blocks to the chain
  • validating new blockschains
  • replacing current blockchain with a new one (when synchronizing with other nodes)

The code for the blockchain class is in src/blockchain/index.ts. The following code snippet shows the storage of an array of blocks within the Blockchain class, which illustrates that when a new blockchain is created, it comes pre-loaded with a single block – the genesis block:


1
2
3
4
5
6
7
export default class Blockchain {
    chain: Block [];

    constructor() {
        this.chain = [Block.getGenesisBlock()];
    }
}

Blockchain Replication

Every node on the network needs to have the same copy of the blockchain. So what happens when a new block gets mined on one node? Now the system has to replicate the new block to all the other nodes on the network so every node is in sync and has the same copy of the blockchain.

Since the blockchain needs to be replicated among all nodes in the network, the question arises – what happens if two nodes mine a block at the same time? This is when there’s a fork in the blockchain and the system needs to reconcile the fork and merge all the new blocks.

blockchain-mining-collision

When a fork occurs, some nodes will get replicated from node A and some will get replicated from node B which will create the following scenario:

blockchain-mining-fork-replication

We will use the rule that the longest chain will be accepted as the main chain. If there is a conflict between multiple nodes mining blocks at the same time, eventually, one chain will be longer and that will be accepted as the master blockchain. The other blocks that were mined on the shorter chain will be incorporated into the main blockchain.

blockchain-mining-fork-longer

Web Application

The web application expose a series of JSON endpoints that will allow each node on the network to interact with the blockchain with its own HTTP server:

  • GET /blocks – view node’s copy of the full blockchain
  • GET /balance – view node’s wallet balance
  • GET /public-key – view node’s public key (to know the wallet address to transfer cryptocurency to)
  • GET /transactions – view node’s copy of the transaction pool (transactions that haven’t been mined)
  • POST /mine – to add a generic new block to the chain (for adding non cryptocurrency blocks to the blockchain)
  • POST /mine-transactions – to add cryptocurrency transactions to the blockchain
  • POST /transact – add cryptocurrency transaction to the transaction pool

The code for the web application is in src/app/index.ts:

Peer-to-Peer Server

Each node on the blockchain network needs the ability to communicate with other nodes (peers) on the network in order to:

  • get an updated blockchain from its peers
  • for broadcasting to other nodes when it has an updated version of the blockchain

Each peer-to-peer server uses web sockets instead of HTTP to communicate with each node.

blockchain-peer-to-peer-server

 

Blockchain synchronization between nodes is done by sending a synchronization message to other nodes as soon as new block is mined on any node. However, before a node replaces its blockchain from an update it received from one of its peers, it needs to check that the updated chain is longer than its current chain and that the new blockchain is valid (by validating its hashes). For new nodes that just join the network, they get an updated version of the blockchain from one of its peers.

The peer-to-peer server is also responsible for synchronizing and clearing the transaction pool between nodes.

The code for the peer-to-peer server is in src/app/p2p-server.ts, which contains the code for blockchain synchronization between nodes:


1
2
3
4
5
6
7
8
9
10
11
/**
 * Convenience method for sending blockchain on a socket.
 * Specifies that message contains blockchain by setting "type" parameter.
 * @param webSocket The WebScoket to send the blockchain on.
 */

sendChain(webSocket: WebSocket): void {
  webSocket.send(JSON.stringify({
    type:MESSAGE_TYPES.chain,
    chain: this.blockchain.chain
  }));
}

The peer-to-peer server is also responsible for processing incoming messages from other nodes and determining what to do, based on the message type. There are three message command types:

  • replace the current blockchain with a new blockchain
  • update or add a new transaction in the transaction pool
  • clear the transaction pool

Code snippet for processing incoming messages:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Process incoming message, based on the message type.
 * @param socket Socket of incoming message.
 */

messageHandler(socket: WebSocket): void {
  socket.on("message", message => {
    const messageData = JSON.parse(message.toString());

    switch(messageData.type) {
      case MESSAGE_TYPES.chain:
        this.blockchain.replaceChain(messageData.chain);
        break;
      case MESSAGE_TYPES.transaction:
        this.tp.updateOrAddTransaction(messageData.transaction);
        break;
      case MESSAGE_TYPES.clear_transactions:
        this.tp.clear();
        break;
      default:
        throw new Error("Undefined message type: " + messageData.type);
    }
  });
}

Proof of Work – Blockchain Mining

A blockchain’s uses proof-of-work as a way to ensure only valid blocks are added to a blockchain and to prevent dishonest nodes from adding blocks. Due to the decentralized nature of a blockchain, any node has a copy of the entire blockchain and can theoretically, add another block. Using proof-of-work makes it computationally expensive to add corrupt blocks.

Difficulty Level

There are different proof-of-work systems, but our blockchain uses a similar proof-of-work system that Bitcoin uses, Hashcash. The goal is to generate a hash with a certain number of leading zeros, which is the difficulty level.

For example, if the difficulty level is three, then the miner needs to generate a hash with three leading zeros before being allowed to add a new block to the chain. The higher the difficulty level, the longer it will take to generate a hash that meets that criteria.

Nonce

The nonce value is iterated by one on every hash generation attempt. If the hash value satisfies the difficulty level, we save the nonce value in the block so other nodes can quickly validate the hash.

51% Attack

A 51% attack could result if a dishonest party could control more than 50% of the computer nodes on the network resulting in the ability to compute hashes faster than any other nodes on the network. This could result in corruption of the blockchain and double spending. The more nodes there are on the network, the less likely this is to happen.

Dynamic Difficulty Level

The blockchain uses a dynamic difficulty level by increasing the difficulty level if blocks are mined too quickly and decreasing the difficulty level if blocks are mined too slowly. The blockchain’s default mine rate is 3000 milliseconds and the difficulty level is adjusted based on whether blocks are mined faster or slower than this value.

Our blockchain supports the idea of a dynamic difficulty level that changes over time, depending on how quickly blocks are added to the blockchain. Each block stores the difficulty level and the mine rate, which represents the rate at which blocks should be mined. Difficulty level will be adjusted by checking the difference between the current and previous blocks and increasing the difficulty level if the difference is below the target mine rate (to increase mining time) and decreasing the difficulty level if the difference is above the target mine rate (to decrease mining time).

blockchain-adjust-mine-rate-dynamic-difficulty

Block mining is implemented in implemented src/blockchain/block.ts. The following code adjusts the difficulty level dynamically and makes use of the nonce when generating hashes for the proof of work.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * Mines new block that will be added to the blockchain.
 * @param lastBlock Link to the previous block for storing its hash.
 * @param data Data to store for the new block.
 */

static mineNewBlock(lastBlock: Block, data: any): Block {
  let timestamp: number;
  const lastHash: string = lastBlock.hash;
  let nonce:number = 0;
  let hash:string;
  let {difficulty} = lastBlock;
  //PROOF OF WORK - keep generating new hashes until get specific number of leading 0's
  do {
    timestamp = Date.now();
    difficulty = Block.adjustDifficulty(lastBlock, timestamp);

    nonce++;
    hash = Block.generateHash(timestamp, lastHash, data, nonce, difficulty);
  } while(hash.substr(0, difficulty) !== "0".repeat(difficulty));
  return new this(timestamp, lastHash, hash, data, nonce, difficulty)
}

Cryptocurrency App

On top of our blockchain, we have a cryptocurrency app that leverages the blockchain to allow transferring digital currency between individuals. Our cryptocurrency app consists of:

  • Wallet object for each user
  • Public-private key pair for digital signatures and verifying signatures from other people
  • Transaction objects to represent the currency exchange between parties

Cryptocurrency Wallet

Our cryptocurrency app uses a wallet to store a user’s balance and the public-private key pair. The private key is used to generate digital signatures and the public key is used to verify signatures from other people. The public address of the wallet is just the public key. The key pair is generated with secp256k1 elliptical algorithm, the same algorithm used by Bitcoin.

The wallet class is implemented in src/wallet/index.ts. Here is a snippet that shows the wallet properties and constructor. Note that each wallet starts with a pre-set balance.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export default class Wallet {
    balance: number;
    keypair: any;
    publicKey: any;
    static bcWallet: Wallet;

    //for balance re-calculation - need to know from where to start recalculating
    lastBlockTimestamp: number;

    //when last did balance recalculation, this was the last block
    lastBlockBalanceCalc: number;

    constructor() {
        this.balance = config.INITIAL_BALANCE;
        this.keypair = ChainUtil.genKeyPair();
        this.publicKey = this.keypair.getPublic().encode("hex");
        this.lastBlockTimestamp = 0;
        this.lastBlockBalanceCalc = 0;
    }
}

Blockchain Wallet

The blockchain wallet is implemented in the Wallet class as a Singleton so the same wallet instance can be reused. The blockchain wallet code is in src/wallet/index.ts:


1
2
3
4
5
6
7
8
9
10
11
/**
 * Uses Singleton pattern to retrieve the special Blockchain Wallet.
 * Creates it only one time.
 */

static getBlockchainWallet():Wallet {
  if(!Wallet.bcWallet) {
    Wallet.bcWallet = new this();
    Wallet.bcWallet.publicKey = config.BLOCKCHAIN_WALLET_ADDRESS;
  }
  return Wallet.bcWallet;
}

Cryptocurrency Transactions

Each block in the blockchain will store one or more cryptocurrency transactions. Since the blockchain is a permanent record of all transactions, each user can see a complete history of their transactions on the blockchain. Each transaction object consists of:

  • a unique ID to identify the transaction object (to make transactions easier to find in large collections)
  • a single Transaction Input object
  • an array of Transaction Output objects

Code for the Transaction class is in src/wallet/transaction.ts. Here is a snippet of the properties of the Transaction class:


1
2
3
4
5
6
7
8
9
10
export default class Transaction {
    id: string;
    txInput: TransactionInput;
    txOutputs: TransactionOutput[];

    constructor() {
        this.id = ChainUtil.genID();
        this.txOutputs = [];
    }
}

Each Transaction Input object contains:

  • the sender’s address (which is their public key)
  • sender’s starting balance before the transaction
  • timestamp
  • sender’s signature of the transaction

Code for the Transaction Input class is in src/wallet/transaction-input.ts:


1
2
3
4
5
6
7
8
9
10
export default class TransactionInput {
    amount: number;
    address: string;
    timestamp: number;
    signature: string;
    constructor(amount: number, address: string) {
        this.amount = amount;
        this.address = address;
    }
}

Each Transaction Output object contains:

  • recipient of transaction (i.e. the other wallet)
  • amount of currency transferred to recipient

Code for the Transaction Output class is in src/wallet/transaction-output.ts:


1
2
3
4
export default class TransactionOutput {
    amount: number;
    address: string;
}

cryptocurrency-transaction-object

Wallet Balance Calculation

To avoid double counting, the balance of each wallet is re-calculated whenever new blocks are added (mined) to the blockchain. The wallet adds all transfers by other wallets to the node’s wallet (since the last time blocks were mined) and subtracts all transfers from the node’s wallet to other wallets (since the last time blocks were mined). This is much faster than having to recalculate the balance by examining the entire blockchain which would get slower as the blockchain grows.

The following diagram shows two transactions. The transaction on the left was initiated by wallet 0xfoo1 and the transaction on the right by wallet 0xzag3. To calculate the balance of wallet 0xfoo1, we subtract the tokens sent by 0xfoo1 (40) and add tokens sent to 0xfoo1 (20) by other wallets. This would mean the balance of 0xfoo1 is 500 (starting balance) – 40 + 20 = 480.

cryptocurrency-transactions-wallet balance-calculations

We only do this balance calculation using the most recently mined blocks. The balance calculation is implemented src/wallet/index.ts:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
 * Calculates wallet balance by:
 * - only checking new blocks mined (added) since last time balance was calculated
 * - divides up transactions into those that transfered money TO this wallet and FROM this wallet
 * Sets the balance of this wallet after calculating it, so won't have to re-calculate next time
 * if blockchain hasn't changed - will be super quick
 * @param blockchain Blockchain to use for calculating the wallet balance.
 */

calculateBalance(blockchain: Blockchain): number {
  this.lastBlockTimestamp = blockchain.chain[blockchain.chain.length-1].timestamp;
  let balance = this.balance;
  const newTransactions: Transaction [] = [];

  //balance already up to date, no need for recalculation
  if(this.lastBlockBalanceCalc === this.lastBlockTimestamp &&
    this.lastBlockBalanceCalc > 0) { //balance already calculated at least once
    return balance;
  }

  //start from end of blockchain to find where to start recalculating from
  //as blockchain grows, won't waste time rechecking old blocks
  let startBlockIndex = 0;
  let blocks: Block [] = blockchain.chain;
  for(let i=blocks.length-1; i>=0; i--) {
    if(blocks[i].timestamp === this.lastBlockBalanceCalc) {
      //calculation should start from 1 block AFTER the last block used to calculate balance
      startBlockIndex = i + 1;
      break;
    }
  }
  //only add transactions from new blocks mined since last time calculated balance
  for(let i=startBlockIndex; i<blocks.length; i++) {
    let blockTransactions: Transaction [] =  blocks[i].data;
    for(let j=0; j<blockTransactions.length; j++) { newTransactions.push(blockTransactions[j]); } } //find all of this wallet's input transactions - i.e. withdrawals to other wallets const thisWalletWithdrawalTxs = newTransactions.filter( transaction => transaction.txInput.address === this.publicKey);

  //find all of this wallet's output transactions (where it's not in the input transaction)- i.e. deposits from other wallets
  const thisWalletDepositTxs = newTransactions.filter(
    transaction => {
      //start from index 1 for TransactionOutputs because index 0 holds temporary balance
      for(let i=1; i<transaction.txOutputs.length; i++) {
        if(transaction.txOutputs[i].address === this.publicKey &&
          transaction.txInput.address !== this.publicKey) return true;
      }
      return false;
    });

  //subtract all new withdrawals from this wallet
  for(let i=0; i<thisWalletWithdrawalTxs.length; i++) {
    //start from index 1 for TransactionOutputs because index 0 holds temporary balance
    for(let j=1; j<thisWalletWithdrawalTxs[i].txOutputs.length; j++) {
      balance -= thisWalletWithdrawalTxs[i].txOutputs[j].amount;
    }
  }

  //add all new deposits to this wallet
  for(let i=0; i<thisWalletDepositTxs.length; i++) {
    //start from index 1 for TransactionOutputs because index 0 holds temporary balance
    for(let j=1; j<thisWalletDepositTxs[i].txOutputs.length; j++) {
      if(thisWalletDepositTxs[i].txOutputs[j].address === this.publicKey) {
        balance += thisWalletDepositTxs[i].txOutputs[j].amount;
      }
    }
  }

  //set so next time won't have to re-check any block if blockchain unchanged
  this.lastBlockBalanceCalc = this.lastBlockTimestamp;
  this.balance = balance;
  return balance;
}

Digital Signatures and Verifying Transactions

Transactions are signed by the sender’s private key and then verified by the recipient with the sender’s public key. The sender first hashes the data and encrypts the hash with their private key. The recipient decrypts the encrypted hash with the sender’s public key and verifies that the decrypted hash is the same value as hashing the data without any encryption. The following diagram (from Docusign) illustrates how this works:

digital-signature-verification

Since the Transaction itself holds the signature (in the Transaction Input object), we can’t sign the entire Transaction object, so instead we sign the hash value of all the Transaction Output objects and store the signature in the Transaction Input object.

The code for signing and verifying transactions is in src/wallet/transaction.ts:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * Used by sender - signs transaction and generates a TransactionInput object.
 * The signature is based on the hash of the TransactionOutput array.
 * @param transaction Transaction to sign. Only the outputItems will be signed.
 * @param senderWallet Wallet to use for signing.
 */

static signTransaction(transaction: Transaction, senderWallet: Wallet): void {
  transaction.txInput = {
    timestamp: Date.now(),
    amount: senderWallet.balance,
    address: senderWallet.publicKey,
    signature: senderWallet.sign(ChainUtil.genHash(transaction.txOutputs))
  };
}

/**
 * Used by receiver - verifies transaction.
 * @param transaction Transaction to verify, based on its signature, hash and TransactionOutput objects.
 */

static verifyTransaction(transaction: Transaction): boolean {
  return ChainUtil.verifySignature(
    transaction.txInput.address,
    transaction.txInput.signature,
    ChainUtil.genHash(transaction.txOutputs)
  );
}

Transaction Updates

When sending tokens to multiple recipients, instead of creating new Transaction objects we can optimize the process by updating the Transaction Output objects within an existing Transaction object. This is done to reduce redundancy of having multiple Transaction objects with the virtually the same Transaction Input objects.

The code for updating transactions is in src/wallet/transaction.ts:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * Allows sender to send funds to multiple recipients by updating the transaction with additional
 * TransactionOutput objects.
 * @param senderWallet Wallet that will be updated.
 * @param recipient Address of additional recipient.
 * @param amountToTx Amount to transfer to new recipient. Throws an error if it exceeds balance.
 * @returns The updated transaction.
 */

update(senderWallet: Wallet, recipient: string, amountToTx: number): Transaction {
  //find the TransactionOutput we need to update
  const senderTxOutput =  this.txOutputs.find(
    txOutput => txOutput.address === senderWallet.publicKey);

  if(amountToTx > senderTxOutput.amount) {
    throw new RangeError("Amount " + amountToTx + " exceeds balance.");
  }

  //reduce the sender's balance by the amount being transferred
  senderTxOutput.amount -= amountToTx;

  //add additional TransactionOutput object to list
  this.txOutputs.push({ amount: amountToTx, address: recipient});
  Transaction.signTransaction(this, senderWallet);
  return this;
}

Transaction Pool

Before transactions are committed to the blockchain, they are first put into a common transactions pool. At this point these transactions are “unconfirmed”. Blockchain miners take the unconfirmed transactions from the pool and add them to the blockchain, thereby confirming them. Once transactions are mined (added) to the blockchain, the transaction pool is cleared. Each node has a replica of the transaction pool, just like the blockchain itself. When there are updates to the pool, the blockchain network communicates with each node on the network to update its transaction pool.

The code for Transaction Pool class is in src/wallet/transaction-pool.ts. Here is a snippet of the properties of a transaction pool, which is just an array of transactions. Note how the transaction pool starts out with no transactions:


1
2
3
4
5
6
7
export default class TransactionPool {
    transactions: Transaction [];

    constructor() {
        this.transactions = [];
    }
}

blockchain-transaction-pool

Miners

Miners have the role of taking transactions from the transaction pool and doing the computationally expensive proof-of-work to add transactions to the blockchain. The transactions go from “unconfirmed” (when they are in the Transaction Pool) to “confirmed”, when they are added to the blockchain. The incentive for miners is that they get rewarded with payment, usually with the currency of the blockchain. For example, Bitcoin miners get Bitcoin payment for mining new blocks.

The Miner class is responsible for:

  • processing valid transactions from the transaction pool
  • rewarding the miner
  • creating a new block on the blockchain
  • informing other peers when a new block has been mined
  • clearing its transaction pool
  • broadcasting to other miners to clear their transaction pools

The Miner class is implemented in src/app/miner.ts:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * Mines a new transaction on the blockchain by:
 * - Validating transactions on the transaction pool
 * - Rewarding the miner
 * - Creating new block on the blockchain consisting of the newly validated transactions
 * - Synchronizes blockchains between all other peers
 * - Clears transaction pool
 * - Broadcasts to every miner to clear their transaction pool
 */

mine(): Block {
  const validTransactions: Transaction [] = this.tp.validTransactions();
  validTransactions.push(Transaction.newRewardTransaction(this.wallet, Wallet.getBlockchainWallet()));
  let block: Block = this.blockchain.addBlock(validTransactions);
  this.p2pServer.syncChains();
  this.tp.clear();
  this.p2pServer.broadcastClearTxs();
  return block;
}

Miners are rewarded with cryptocurrency from the network by a special blockchain wallet. The blockchain wallet has the ability to send reward transactions to miners without reducing its own balance (unlike regular wallets).

Generating miner reward transactions is implemented in the Transaction class in src/wallet/transaction.ts:


1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * Generates reward transaction for miners. Added to array TransactionOutputs within the transaction.
 * @param minerWallet Miner's wallet.
 * @param blockchainWallet Blockchain's wallet.
 * @returns The reward transaction for the miner.
 */

static newRewardTransaction(minerWallet:Wallet, blockchainWallet:Wallet): Transaction {
  let txOutputs: TransactionOutput [] = [
    { amount: 9999999,              address: config.BLOCKCHAIN_WALLET_ADDRESS},
    { amount: config.MINING_REWARD, address: minerWallet.publicKey}
  ];
  return Transaction.transactionsWithOutput(blockchainWallet, txOutputs);
}

Conclusion

This blockchain is working but is just a prototype. Further enhancements would need to be made in order to really make this blockchain production ready. Nonetheless, it has a lot of the main features of major blockchains like Bitcoin and Ethereum which I tried to summarize and demonstrate.