In this tutorial, you’ll learn how to easily deploy Proof of Stake blockchain applications to an IBM Container as two validator nodes using Tendermint and LotionJs.

Learning objectives

  • Learn how to use Tendermint and LotionJs to deploy Proof of Stake blockchain application to an IBM Container as two validator nodes

Time to complete this tutorial

  • Approximately 60 minutes.

Prerequisites

You need the following software installed:

  • IBM Cloud CLI, the command-line interface to manage applications, containers, infrastructures, services, and other resources in IBM Cloud
  • Docker, the platform for developing, deploying, and running applications with containers
  • Kubectl, the command-line tool to deploy and manage applications on Kubernetes

Background

Blockchain technology is one the greatest technological innovations since the internet. The internet empowers the free flow of information, while blockchain empowers the frictionless flow of value. The amount of research going on in applied cryptography is unprecedented. And companies across the spectrum of industries are adopting blockchain technology for real-world business use cases.

Blockchain is also a very new technology. It’s challenging to do a rapid prototype of a blockchain application and run it on the cloud. Tendermint allows you to rapidly create blockchain applications because it handles the underlying blockchain layers such as networking and consensus, and allows you to focus on the application layer through ABCI (Application Blockchain Interface). In this tutorial, I show you how to easily create a blockchain chat application and deploy it to the IBM Cloud using Tendermint and Lotionjs.

What is Tendermint

Simply put, Tendermint is software that can be used to achieve Byzantine Fault Tolerance (BFT) in any distributed computing platform. From managing infrastructure to designing a distributed database, you can use Tendermint. Tendermint is built on two important technical components:

  • Consensus Engine (also known as Tendermint core) ensures that each transaction is recorded in every machine in the same order through Proof of Stake consensus.
  • ABCI (Application Blockchain Interface) enables transactions to be processed in any programming language.

Tendermint has these properties:

  • Byzantine Fault Tolerant: Tendermint tolerates up to one third of your machines failing arbitrarily. This includes explicitly malicious behavior.
  • State machine replication: Tendermint can replicate deterministic state machines written in any programming language.
  • Secure P2P: Gossip protocols and peer discovery are secured via Tendermint’s authenticated encryption system.
  • Lightning fast: Tendermint supports thousands of transaction per second at 1000ms latencies.

What is Lotionjs

Lotion is a fast and fun way to create blockchain apps in JavaScript. It builds on top of Tendermint using the ABCI protocol.

1

Set up the project

  1. Create a new directory and navigate to it:
    
    $ mkdir blockchain
    $ cd blockchain
    
  2. Install the required libraries:
    
    $ npm i lotion ‑‑save
    $ npm i dotenv ‑‑save
    
2

Create private key and genesis file of 2 validator nodes

  1. Create the genesis file:
    
    touch genesis.json
    
  2. Populate initial content to genesis.json:
    
    {
       "genesis_time": "0001‑01‑01T00:00:00Z",
       "chain_id": "name",
       "validators": [
       ],
       "app_hash": ""
    }
    
  3. Generate keys of 2 validator nodes:
    
    $ ./node_modules/lotion/bin/tendermint gen_validator > privkey0.json
    $ ./node_modules/lotion/bin/tendermint gen_validator > privkey1.json
    
    Private keys look like this, with public and private keys randomly generated:
    
    {
        "address": "B809574EC51377DE48454094BF3302989CBB50A9",
        "pub_key": {
            "type": "ed25519",
            "data": "8A049817BA6D1B065C30D927A529AAFA7147BE0D147E1CCD7A25FAADBE80C8D0"
        },
        "priv_key": {
            "type": "ed25519",
            "data": "57BAFDD6136E1140FA9F906313BF2CFC75802F044704DD7AAF30BC1010E6519C8A049817BA6D1B065C30D927A529AAFA7147BE0D147E1CCD7A25FAADBE80C8D0"
        }
    }
    
  4. Copy only the public key information and paste it in genesis.json. It will look like this, after you add two validators:
    
    {
        "genesis_time": "0001‑01‑01T00:00:00Z",
        "chain_id": "name",
        "validators": [
            {
                "pub_key": {
                    "type": "ed25519",
                    "data": "8A049817BA6D1B065C30D927A529AAFA7147BE0D147E1CCD7A25FAADBE80C8D0"
                },
                "power": 10,
                "name": "saif"
            },
            {
                "pub_key": {
                    "type": "ed25519",
                    "data": "5FD1FBF59759E50BD1C23911E832198AB78A4F7E6F1F23A64AAFEC5992608CA8"
                },
                "power": 20,
                "name": "prerna"
            }
        ],
        "app_hash": ""
    }
    
3

Build validator 1

  1. Create the node1 folder:
    
    mkdir node1
    
  2. Navigate to the node1 directory:
    
    cd node1
    
  3. Install the required libraries:
    
    $ npm i lotion ‑‑save
    $ npm i dotenv ‑‑save
    
  4. Copy genesis.json and privkey0.json generated previously to the node1 folder.

  5. Create the environment file of node1:
    
    $ touch .env‑node1
    
  6. Code the content of node1.js:
    
    LOTION_HOME="./.lotion_node1"
    
  7. Create node1.js:
      
    $ touch node1.js
    
  8. Code the content of node1.js:
     
    require('dotenv').config({path: ".env‑node1"});
    let lotion = require('lotion')
    let app = lotion({
      genesis: './genesis.json',
      tendermintPort: 30090,
      initialState: { messages: [] },
      p2pPort: 30092,
      logTendermint: true,
      keys: 'privkey0.json',
      peers: 'workernode2:30092'})
    app.use((state, tx,chainInfo) => {
      if (typeof tx.sender === 'string' && typeof tx.message === 'string') {
        state.messages.push({ sender: tx.sender, message: tx.message })
      }
    })
    app.listen(3000).then(({ GCI }) => {
      console.log(GCI)
    })
    
  9. Create the Dockerfile:
             
    $ touch Dockerfile
    
  10. Code the content of the Dockerfile:
    
    FROM node:carbon
    WORKDIR /usr/src/app
    COPY package*.json ./
    COPY privkey0.json ./
    COPY .env‑node1 ./
    RUN npm install
    COPY . .
    EXPOSE 30090 30092
    CMD  "node", "node1.js" 
4

Build validator 2

  1. Create the node2 folder:
    
    mkdir node2
    
  2. Navigate to the node2 directory:
    
    cd node2
    
  3. Install the required libraries:
    
    $ npm i lotion ‑‑save
    $ npm i dotenv ‑‑save
    
  4. Copy genesis.json and privkey0.json generated previously to the node2 folder.

  5. Create the environment file of node2:
    
    $ touch .env‑node2
    
  6. Code the content of node2.js:
    
    LOTION_HOME="./.lotion_node2"
    
  7. Create node2.js:
      
    $ touch node1.js
    
  8. Code the content of node2.js:
     
    require('dotenv').config({path: ".env‑node2"});
    let lotion = require('lotion')
    let app = lotion({
      genesis: './genesis.json',
      tendermintPort: 30090,
      initialState: { messages: [] },
      p2pPort: 30092,
      logTendermint: true,
      keys: 'privkey1.json',
      peers: 'workernode1:30092'})
    app.use((state, tx,chainInfo) => {
      if (typeof tx.sender === 'string' && typeof tx.message === 'string') {
        state.messages.push({ sender: tx.sender, message: tx.message })
      }
    })
    app.listen(3000).then(({ GCI }) => {
      console.log(GCI)
    })
    
  9. Create the Dockerfile:
             
    $ touch Dockerfile
    
  10. Code the content of the Dockerfile:
    
    FROM node:carbon
    WORKDIR /usr/src/app
    COPY package*.json ./
    COPY privkey0.json ./
    COPY .env‑node1 ./
    RUN npm install
    COPY . .
    EXPOSE 30090 30092
    CMD  "node", "node2.js" 
5

Deploy two validator nodes on the IBM Cloud

  1. Sign up for the IBM Cloud.

  2. Install the Cloud Foundry CLI.

  3. Install the IBM Cloud CLI.

  4. Install the Kubernetes CLI.

  5. Navigate to the node1 directory:
            
    $ cd node1
    
  6. Build the Docker image:
    
    $ docker build ‑t node1 .
    
  7. Go to the IBM Cloud console.

  8. Navigate to Containers: figure1
  9. Click Create Cluster: figure2
  10. Name your cluster and create a cluster; wait for few minutes to get it fully deployed. After it is fully deployed, click on the cluster to see an overview of your deployed cluster: figure3
  11. Click Access in the left nav and follow the instructions.

  12. Install the container service plugin:
        
    $ bx plugin install container‑service ‑r Bluemix
    
  13. Log in to your IBM Cloud account:
    
    $ bx login ‑a https://api.eu‑de.bluemix.net
    $ bx cs region‑set eu‑central
    
  14. Set the context for the cluster in your CLI: a. Get the command to set the environment variable, and download the Kubernetes configuration files:
    
    $ bx cs cluster‑config NameOfYourCluster
    
    b. Set the KUBECONFIG environment variable. Copy the output from the previous command and paste it in your terminal.
  15. Verify that you can connect to your cluster by listing your worker nodes:
                    
    $  kubectl get nodes  
    
  16. Go to your IBM Cloud Container Registry.

  17. Choose a name for your first namespace, and create that namespace:
    
    $ bx cr namespace‑add <my_namespace>
    
  18. Log your local Docker daemon into the IBM Cloud Container Registry:
    
    $ bx cr login
    
  19. Choose a repository and tag by which you can identify the image. Use the same repository and tag for the rest of this tutorial.
    
    $ docker tag node1 <registry>/<my_namespace>/node1:latest
    
  20. Push the image.
        
    $ docker push <registry>/<my_namespace>/node1:latest
    
  21. Verify that your image is in your private registry:
     
    $ bx cr image‑list
    
  22. Now that your container is deployed on IBM Container, it’s time to spin up some pods using Kubernetes. Change an image name from the private name of your registry using copy/paste. (The image attribute in the service-deployment.yml file will be same as the image name in the private registry.)
    
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
     name: tendermintnodeone #give any name
    spec:
     replicas: 1
     template:
       metadata:
         name: tendermintnodeone
         labels:
           run: tendermint1
       spec:
         containers:
           ‑ name: saif1cluster
             image: "<registry>/<my_namespace>/node1:latest" #your registry name
             imagePullPolicy: Always
    ‑‑‑
    apiVersion: v1
    kind: Service
    metadata:
     name: my‑service‑tendermint‑11 #give a service name
     labels:
       run: tendermint1
    spec:
     type: NodePort
     selector:
       run: tendermint1
     ports:
      ‑ protocol: TCP
        name: tendermint1
        port: 30090  
        nodePort: 30090  
      ‑ protocol: TCP
        name: port
        port: 30092  
        nodePort: 30092 
    
  23. Configure Kubernetes, to create pods, services, and deployments:
    
    $ kubectl create ‑f service‑deployment.yml
    
  24. Delete all deployments:
    
    $ kubectl delete deployments ‑‑all
    
  25. Delete all services:
    
    $ kubectl delete services ‑‑all 
    
  26. Access pods and logs:
    
    $ kubectl get pods
    $ kubectl logs podname
    
  27. Access Tendermint RPC [APIs[(http://workerip:30092).

Repeat the same steps for node2.

6

Create a light client to read/write on the blockchain

  1. Install lotion-connect:
    
    $ npm install lotion‑connect ‑‑save
    
  2. Create write.js:
    
    $ touch read‑write.js
    
  3. Code the content of read-write.js:
    
    let { connect } = require('lotion‑connect')
    async function main() {
    let { state, send } = await connect(null, { 
      genesis: require('./genesis.json'),
      nodes:  'ws://wordernode1:30092','ws://wordernode2:30092' })
    console.log(await state)
    console.log(await send({ "sender": 'saif',"message":"himom" }))
      process.exit();
    }
    main()
    

Conclusion

Tendermint makes it easy for business developers to write blockchain applications, and the IBM Cloud makes it easy to deploy and operate such applications.

Learn more

Next steps

  • Now that you’ve deployed a blockchain application to the IBM Cloud, learn how to create business networks from scratch using Hyperledger Composer in our popular series, Hyperledger Composer basics, by Steve Perry.

  • Stay in the know with the monthly Blockchain Newsletter for developers. Check out the current issue and subscribe.

  • Check out the many code patterns, which provide roadmaps for solving complex problems with blockchain technology, and include architecture diagrams, code repos, and additional reading.

  • Stop by the Blockchain Developer Center. It’s your source for free tools and tutorials, along with code and community support, for developing and deploying blockchain solutions for business.

About the author

Saif Rehman has a degree in Computer Engineering from the University of Sharjah, and will be pursuing a Master’s degree in Computer Science at Georgia Tech. Saif is a Software Developer/Tester and Python geek who likes following agile methodologies. With his strong grasp of programming fundamentals, he learns new programming language and frameworks quickly. He has invested much of his time in front-end technologies like Angular and Elixir, and in developing secure and scalable Web APIs. He likes exploring new technologies like Blockchain, and is pursuing his career as a Data Scientist now.