Implementing Proof of Stake Part — 4

This article presents an easy implementation of Proof of Stake consensus algorithm in node.js

Kashish Khullar
Coinmonks
Published in
9 min readFeb 1, 2019

--

In the previous post, we created a basic p2p-server and an API to interact with our blockchain. In this part, we’ll further extend our project and we will implement a wallet.

The Wallet

To create a cryptocurrency we need a wallet. Wallet will have two keys, namely public and a corresponding private key. Public/private key pairs are a part of asymmetric cryptography which we will use to create digital signatures.

Digital signatures allow users to verify the miner which created the block and the transaction. Once a data is signed/encrypted using the private key it can only be verified/decrypted using the public key.

Create a separate folder wallet in the root directory and add a wallet.js file in it where we will create our wallet class.

Our wallet will have 3 main properties, the balance, a keypair, and the publicKey.

Note: I have passed the variable secret as a parameter to the constructor. We will pass one when we create a wallet.

To create a key pair (public key and private key ) we’ll use a module called elliptic. Lets install it.

npm i elliptic --save

Lets create a new file called chain-util.js in the root directory, which will hold the functions related to cryptocurrency. We’ll use this module that we just installed, we’ll use the use the ec curve based algorithm in our app. So in chainUtil.js require eddsa algorithm from elliptic and create an instance using ed25519

const EDDSA = require("elliptic").eddsa;
const eddsa = new EDDSA("ed25519");

Let's create a function in the chainUtil class which we will be using in our app to generate a key pair.

class ChainUtil {  static genKeyPair(secret) {    return eddsa.keyFromSecret(secret);  }}module.exports = ChainUtil;

Now we can use this function in our wallet class, and use the getPublic() function to get the public an encode it to hexadecimal format.

this.keyPair = ChainUtil.genKeyPair();this.publicKey = this.keyPair.getPublic('hex');

Our basic wallet is ready. Time to make transactions.

Transactions

Our transactions will have the following structure—

{
id: <here goes some identifier>
type: <transactions type: stake,validator,transaction>
input: {
timestamp: <time of creation>,
from: <senders address>,
signature: <signature of the transaction>
}
output: {
to: <recievers address>
amount: <amount transfered>
fee: <transactions fee>
}
}

Each transaction needs a unique id. To generate these unique numbers we will use a module called uuid.

npm i uuid --save

Lets create an instance of this module in the chain-util.js file.

const uuidV1 = require('uuid/v1');// version 1 use timestamp to generate unique ids, although in production one shouldn't use this

Lets create a function to generate ids.

static id(){
return uuidV1();
}

Now that we can uniquely identify transactions. We can create our transactions class.

Input and output will be an object as described above.

In the output object also has a field of fee To add transaction fee, we will create a constant and add use it in generateTransaction() function. Since we might want to change the fee later in future we’ll keep this contant in a separate file.

Create a config.js in the root folder and add a constant variable TRANSACTION_FEE in it.

config.js file

Lets create a function to create a transaction. This function will take the sender’s wallet’s instance, the amount to send and the recipient's address.

It will check if the sender has enough balance and then create a transaction object which will have all the properties passed to the function. To keep the code modular we’ll divide the function into two. generateTransaction will actually serve the same function but will look much neater.

Our transaction now needs an input object. We’ll use the wallets key pair to sign the transaction and add the signature in the input object along with the other relevant information.

Lets add a sign function in the wallet/index.js file

sign(dataHash){   return this.keyPair.sign(dataHash);}

We’ll use this function in the transaction.js file when we create new transaction. Lets add a function in the transaction class called signTransaction() This function will take transaction and senders wallet’s instance and create a signed input in the transaction.

Note that we are using hashing function quite a number of times. We can abstract this out into chain-util.js to make are code more reusable. Lets create a hash(data) function in chainUtil and use this instead of directly using SHA256

static hash(data){   return SHA256(JSON.stringify(data)).toString();}

Also make these changes in the block.js file.

Alright next we will create thesignTransaction()function in transaction.js

Now in thegenerateTransaction() function we can call signTransaction() with parameter as the current transaction and the sender's wallet before we return the transaction object.

We also need the functionality to verify the authenticity of the transactions.

Let's create a verifyTransaction() function that will verify the hash and the digital signature of the transaction.

To verify a signature we need to decrypt it using the public. The elliptic module provides this functionality inbuilt. Lets create a function verifySignature() in the ChainUtil class which we will use later.

static verifySignature(publicKey,signature,dataHash){return ec.keyFromPublic(publicKey).verify(dataHash,signature);}

Now in the Transaction class lets create a function called verifyTransaction() where we’ll use the verifySignature function we just created.

Our transaction class is finally complete. Here’s what it will look like.

The Pool

As multiple individuals create transactions with their wallets on the cryptocurrency will need a way to include groups of these transactions we will now use the concept of the transaction-pool. The transaction-pool will be an object updated in real time that contains all the new transactions submitted by all miners in the network.

Users will create transactions and then they will submit every transaction to the pool. These new transactions, present in the pool, will be considered unconfirmed.

Miners take a group of transactions from this pool and create the block and makes the transaction confirmed.

To update our transaction pool we will use the p2p server that we created and broadcast transactions to the network. When we receive a new transaction we will add it to our pool.

But first, let's create transaction-pool.js file in the wallet directory and create a TransactionPool class.

This class will have only one property an array of transactions.

this.transactions = [];

Let's add some methods to the pool.

Add a method to push transactions in the list. Note that we may receive transactions that we may already have so also need to check that beforehand.

Create a function addTransaction(transaction), this method will simply add the transaction to the pool.

const Transaction = require("./transaction");
class TransactionPool {
constructor() {
this.transactions = [];
}
addTransaction(transaction) {
this.transactions.push(transaction);
}
}
module.exports = TransactionPool;

Here is what our pool looks like:

Since our wallet is responsible for creating a transaction we need a function to do so. Create a function to create a new transaction, sign it using our wallet and add it to our transaction pool. We’ll use all the functions that we have created above.

Let's create a createTransction() function in the wallet class which will do this job, since we need to add transactions to the the transaction pool, we need an instance of the transaction pool as a parameter.

To create transactions we need an endpoint. Lets all that we have created into our main app.

Lets make the createTransaction() function

createTransaction(to, amount, type, blockchain, transactionPool) {
let transaction = Transaction.newTransaction(this, to, amount, type);
transactionPool.addTransaction(transaction);
return transaction;
}

In the app/index.js file, create a wallet instance and a transactionPool instance.

const Wallet = require('../wallet');const TransactionPool = require('../wallet/transaction-pool');// create a new walletconst wallet = new Wallet(Date.now().toString());// Date.now() is used create a random string for secret// create a new transaction pool which will be later// decentralized and synchronized using the peer to peer serverconst transactionPool = new TransactionPool();

let's make a get endpoint ‘/transactions’

// api to view transaction in the transaction poolapp.get('/transactions',(req,res)=>{  res.json(transactionPool.transactions);});

Now run the app and open postman

npm run dev

Hit the api and you will get an empty array as the response which we expect.

Lets create post ‘/transact’ to create new transaction

// create transactionsapp.post("/transact", (req, res) => {  const { to, amount, type } = req.body;  const transaction = wallet.createTransaction(     to, amount, type, blockchain, transactionPool  );res.redirect("/transactions");});

Test this api in postman with an address of the recipient and the amount and you’ll see that you can’t create a transaction because your initial balance is zero.

For testing purpose, we can create a constant INITIAL_BALANCE to check if everything is working fine. Add a constant INITIAL_BALANCE in config.js and use in the wallet class constructor. Set the balance as this variable and in config.js setINITIAL_BALANCE as 100.

// wallet.js
const {INITAL_BALANCE} = require('../config');

this.balance = INITIAL_BALANCE
// config.js
INITAL_BALANCE = 100

Now, create a post request with JSON data with fields to, amount and type :

{
“to”:”rand-address”,
“amount”:10,
“type”: “TRANSACTION”
}

Hit send a couple of times and you will get a list of transactions as a very satisfying response.

Transaction present in the pool

Great!!

Broadcasting Transactions

Though we can create transactions in our applications we have to also implement some functionality to send those transactions to other nodes in the network. To implement this we will make use of our p2p-server class.

We can add an instance of our transaction-pool in the p2p-server. This way our server can directly access the transactions.

In P2server class we will add another property in its constructor which is the transaction pool instance.

constructor(blockchain,transactionPool){this.blockchain = blockchain;this.sockets = [];this.transactionPool = transactionPool;}

We also need to update the instance that we create in the app/index.js file and pass the transactioPoolinstance to the p2pserverinstance.

const p2pserver = new P2pserver(blockchain,transactionPool);

Now let's create a method to handle transactions in the p2pserver class. The method will be very similar to this sync chains function that we already have right now which synchronizes the chains of every individual that is connected socket to this application.

To handle multiple message types we’ll create an object MESSAGE_TYPE in the p2pserver class.

const MESSAGE_TYPE = {chain: 'CHAIN',transaction: 'TRANSACTION'}

This method will send the transaction to each connected socket whenever a new transaction is created.

So, let's create a function broadcastTransaction(transaction) in the p2pserver class. This function will send the transaction to each socket. To modularize the code we can create another function called sendTransaction(socket,transaction) just to send a transaction to a single socket and call this function for each socket.

Similarly, we will use the MESSAGE_TYPEs to send the chains as well. So update the sendChain function too.

sendChain(socket){  socket.send(JSON.stringify({  type: MESSAGE_TYPE.chain,  chain: this.blockchain.chain  }));}

Let us also write code to handle transactions sent to us.

Since we have different types of messages we need to handle them differently instead of creating multiple functions we’ll use a switch statement to select a handle based on the message type.

In our message handler for the chain as the data, we’ll call replaceChain function and for data as a transaction, we’ll call addTransaction function.

The above code snippet adds the received transaction to the pool if it does not exists in it. Note that we need to write the function transactionExists() in the transactionPool class.

// transaction-pool.js
transactionExists(transaction) {
let exists = this.transactions.find(t => t.id === transaction.id);return exists;}

After adding the transaction to our pool, we further broadcast it so that all the neighbors of the current node also receive the transaction.

Register this message handler to each socket

// p2p-server.jsconnectSocket(socket){
this.sockets.push(socket);
console.log("Socket connected");
this.messageHandler(socket);
this.sendChain(socket);
}

Finally, we’ll use the broadcast transaction function in our API. When we create a new transaction in the API we’ll call the broadcastTransaction function.

// index.jsapp.post("/transact", (req, res) => {
const { to, amount, type } = req.body;
const transaction = wallet.createTransaction(
to,
amount,
type,
blockchain,
transactionPool
);
p2pserver.broadcastTransaction(transaction);
res.redirect("/transactions");
});

Now the transactions are actually going to broadcast across the network and our message handlers will add them to the transaction pool.

We can test this by opening two terminals and running two app instances. We’ll create a bunch of transactions in one of the terminals and then we’ll hit a get transactions endpoint of the other app instance.

You’ll see that the transactions are actually getting broadcasted to the networks.

Test the functionality by creating transactions for the second instance and see those transactions using the endpoint of the first instance.

Thank you for reading. In the next part, we’ll create an account model, stake and validator list. Hope you enjoyed coding. If you found this helpful throw a bunch of claps.

If you have any questions about blockchain, ethereum or the world in general, leave a comment. :)

--

--