How to build a dapp on a private Ethereum network : Part — 4

0
188

How to build a dapp on a private Ethereum network: Part — 4

This is a series of 5 articles for developing a decentralized application (dapp) on a network of 2 private Ethereum nodes without using any third-party APIs or apps (such as Infura, MetaMask, etc.). This tutorial covers the essential elements of what I learn during my research project at CSIR-CEERI.
I have covered sections I-VI in Parts 1–3. If you feel lost, you can read through the previous 3 parts whose links are mentioned at the bottom of this article in the All series links section.

Table of Contents

Here, you can find a list of the sections and subsections that I have divided this tutorial into :

I. Creating the project

II. Installing prerequisites

III. Configuring the network

IV. Designing the smart contract

V. Setting-up the Truffle project

VI. Launching the network

→ VII. Building the web app

VIII. Testing the dapp

IX. Modifying the dapp

If you want to skip to a particular section, you can scroll down to the All series links section at the end of this article.

The flavor of a dish is all about its taste and feel. A well-cooked dish is truly complemented by the way it is served. A little garnishing will impart a wholesome appeal to it. Nothing fancy though, just some fresh coriander and a spoonful of cream.

By now, you have launched a private Ethereum Network of at least 2 nodes and have deployed the helloworld.sol smart contract onto the network. It is possible to interact with this system via the command line terminal. However, to make the dapp user friendly, you need a GUI.

To this end, we will develop the front-end and link it with the back-end (section VII) with the help of this article.

VII. Building the web app

Here, the focus is not on the design of web pages but on how to integrate a GUI with the private Ethereum network. However, it does help if the project has a nice interface. This is how the web pages would look like :

It’s not pretty, but it gets the job done. Now hurry up, we got code to write!!

1. Front-end : Client Interface

This requires web3 and truffle-contract packages, which in turn need build-essential to be setup. Open a terminal in the TruffleDapp folder and run :

$ sudo apt-get install build-essential
$ npm init
$ npm install truffle-contract@4.0.0-beta.2 --save-dev
$ npm install web3@1.0.0-beta.36 --save-dev

Once the prerequisites are installed, create a folder named client inside the TruffleDapp directory and follow the steps as given.

A. CREATING INDEX PAGE

Create an index.html file inside the TruffleDapp/client repository with the following content :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Sample Dapp</title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<h2>Hello World Ethereum Dapp</h2>
<body>
<div>
<div class="container">
<label for="username" class="col-lg-2 control-label">Enter your name :</label>
<input id="username" type="text"/>
</div>
<div>
<button id="setName">Save Name</button>
<button id="getName">Get Name</button>
</div>
</div>
<div class="container">
<div><label class="center-label" for="output">OUTPUT --> </label>
<div id="output">None</div></div>
</div>
<div class="container">
<div><label class="center-label" for="errorHolder">ERROR --> </label>
<div id="errorHolder">None</div></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="dist/bundle.js"></script>
</body>
</html>

B. ADDING CSS

The HTML files only define a basic skeleton for the web pages. To make them appealing, create a main.css file inside the client repository and type :

h2 {
text-align: center;
padding-bottom: 10px;
}
body {
background-color: rgba(203, 209, 218, 0.877);
padding: 2em;
font-family: 'Verdana';
width: 900px;
margin: auto;
font-size: 15px;
}
div.Hontainer {
width: 100%;
margin: 0 auto;
}
label {
display: inline-block;
padding: 20px;
clear: both;
}
#output{
display: inline-block;
background-color: aquamarine;
padding: 1em 10px;
width: 60%;
}
#errorHolder{
display: inline-block;
background-color: coral;
padding: 1em 10px;
width: 60%;
}
.center-label {
margin: 1em 1em;
}
input {
padding:8px;
width: 60%;
margin-bottom: 0.5em;
}
button {
font-family: 'Verdana';
margin: 1em auto;
padding: 12px 1em;
background-color: #1479F5;
color: white;
font-size: 15px;
}
button:hover {
background-color: rgb(1, 146, 20);
}

This CSS template has been borrowed from a different project, so it is not been tailor-made to suit this project.

4. ADDING JS FUNCTIONALITY

The HTML components used above need to be linked with corresponding functions that execute within the web browser to perform the required functions.

Here, the local private ethereum blockchain is used as web3Provider and TruffleContract from the truffle-contract package is used to access smart contract methods. Inside the client repository, open app.js file in an editor and type :

const Web3 = require('web3');
const TruffleContract = require('truffle-contract');
const request = require('request');
App = {
/*==================================================================
DEFINE ESSENTIALS
==================================================================*/
web3Provider: null,
contracts: {},
currentAccount:{},

// Integrates web3 with our web app
initWeb3 : async function (){
if (process.env.MODE == 'development' || typeof window.web3 === 'undefined'){
App.web3Provider = new Web3.providers.HttpProvider(process.env.LOCAL_NODE);
}
else{
App.web3Provider = web3.currentProvider;
}
web3 = new Web3(App.web3Provider);
return await App.initContract();
},

// Initialises a variable with IDManagement contract JSON
initContract : async function (){
await $.getJSON('HelloWorld.json', function(data){
var hwArtifact = data;
App.contracts.helloworld = TruffleContract(hwArtifact);
App.contracts.helloworld.setProvider(App.web3Provider);
})
return App.bindEvents();
},

// Binds button clicks to the repective functions
bindEvents: function() {
$('#setName').click(App.ExeInputUser);
$('#getName').click(App.CallDispUser);
},
// Defines functionality for OUTPUT label
showMessage: function (msg){
$('#output').html(msg.toString());
$('#output').show();
$('#errorHolder').hide();
},

// Defines functionality for ERROR label
showError: function(err){
$('#errorHolder').html(err.toString());
$('#errorHolder').show();
$('#output').hide();
},
/*==================================================================
USERNAME SUBMISSION
==================================================================*/
ExeInputUser: function (){
var NAME = $('#username').val();
console.log("In app.js ExeInputUser", NAME);

if(NAME) {
// Retrieves user account to perform operations
web3.eth.getAccounts(function (error,accounts){
if (error){
console.log("ERROR getAccounts ExeInputUser");
App.showError(error);
}
App.currentAccount = accounts[0];

// Submits name to the blockchain network
App.contracts.helloworld.deployed().then(function(obj){
return obj.inputUser.sendTransaction(NAME, {from:App.currentAccount});
}).then(function(result){
console.log(result);
App.showMessage("Name submitted as a txn");
}).catch(function (error){
console.log("ERROR ExeInputUser");
App.showError(error);
});
});
}
else {
App.showError("Valid name is required !");
}
},
/*==================================================================
USERNAME RETRIEVAL
==================================================================*/
CallDispUser : function (){
var NAME = $('#username').val();
if(NAME) {
console.log("In app.js CallDispUser");
web3.eth.getAccounts(function (error,accounts){
if (error){
console.log("ERROR getAccounts CallDispUser");
App.showError(error);
}
App.currentAccount = accounts[0];

App.contracts.helloworld.deployed().then(function(instance){
return instance.dispUser.call({from:App.currentAccount});
}).then(function(result) {
console.log("CallDispUser returns : ", result);
App.showMessage(result);
}).catch(function (error){
console.log("ERROR CallRegStatus");
App.showError(error);
})
})
}
else {
App.showError("No output as no name is entered");
}
},
/*==================================================================
INITIALISATION : Intialises web3 when webpage is loaded onto browser
==================================================================*/
init : async function (){
await App.initWeb3();
console.log("In app.js init, initiated App");
}
}

$(function() {
$(window).load(function() {
App.init();
});
});

2. Setting Up Middleware : Express.js

Express.js is a light-weight web application framework which helps organize a web application on the server side and enables management of all facets of a web app, from routes, to handling requests and web pages. The first step is to setup an environment.

  • To install Express.js, open a terminal in the TruffleDapp folder (T1) and run :
$ npm install express --save
  • To define the environment variables, a dotenv package is installed by executing in the same terminal window :
$ npm install dotenv --save
  • Following this, create a .env file in TruffleDapp and type in :
IP = "127.0.0.1"
PORT = 3000
MODE = "development"
LOCAL_NODE = "http://127.0.0.1:8081"

Here, PORT is the port at which we run the Node.js server, MODE is the network variable mentioned in truffle-config.js and LOCAL_NODE is the URL of the local private blockchain.

  • Next, make a server repository inside TruffleDapp. Create a main.js file inside the server folder, open it in an editor and type in :
require('dotenv').config();
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
const IP = process.env.IP || '127.0.0.1';
// Enter the path of YOUR project directory
const proj_dir = `/home/amey/Projects/medium-tutorial`;
app.use(express.static('client'));
app.use(express.static('build/contracts'));
// Serves the home page of the project
app.get('/index', (req, res) => {
res.sendFile(`${proj_dir}/client/index.html`);
});
app.get('*', (req, res) => {
res.status(404);
res.send('Oops... this URL does not exist');
});
app.listen(PORT, IP, () => {
console.log(`HelloWorld Dapp running on port ${PORT}...`);
});
  • Add the following statement under "scripts" of package.json file :
"start": "node server/main.js"
  • To test whether the web pages load as expected, run $ npm run start in T1 and open the URLs http://localhost:3000/index in a web browser. However, note that the web pages will not be functional since there is no geth node running at LOCAL_NODE = "http://127.0.0.1:8081", as mentioned in the .env file.

Note : In app.js, packages and external libraries imported by require cannot be understood by a browser. Hence, webpack is used as a bundler for modules. Its main purpose is to bundle JavaScript files for usage in a browser.

  • First, install the necessary modules. Run in T1 :
$ npm install webpack@4.41.4 webpack-cli --save-dev
  • The environment variable declared in .env cannot be accessed directly on client side. Thus, they are defined here in DefinePlugin. The node and externals configurations are used to fix webpack errors on build. Add a webpack.config.js file with the following webpack configurations :
require('dotenv').config();
const webpack = require('webpack');
const path = require('path');

module.exports = {
entry: './client/app.js',
mode: process.env.MODE,
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'client/dist')
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'LOCAL_NODE': JSON.stringify(process.env.LOCAL_NODE),
'MODE':JSON.stringify(process.env.MODE),
}
})
],
node: {
net: 'empty',
tls: 'empty',
dns: 'empty'
},
externals:[{
xmlhttprequest: '{XMLHttpRequest:XMLHttpRequest}'
}]
};
  • Add the following in "scripts" section of package.json :
"dev" : "node_modules/.bin/webpack && node server/main.js",
"webpack": "node_modules/.bin/webpack --watch"
  • Run $ npm run webpack in T1 to check for any webpack build error. If there is no error, testing the dapp can be started by running $ npm run dev in T1 and then opening the URLs http://localhost:3000/index in a web browser.

So we have built a simple decentralized application complete with an interactive GUI component. We will end this tutorial series with an article on testing the dapp and a highly-useful section on how to modify this dapp.

P.S. — Clap 50 times if you liked the article! Comment below to let me know your thoughts or if you want to share some hacks.

I will be publishing more such interesting articles shortly. Stalk me on Twitter to stay tuned.

All series links

In case you want to skip ahead and jump onto a specific section, you can use the links below for reference. Refer to the Table of Contents below to match a section with its corresponding topic.

Part-1 : Section I-III

Part-2 : Section IV

Part-3 : Section V-VI

Part 4 : Section VII

Part 5 :


How to build a dapp on a private Ethereum network : Part — 4 was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.