Building dapps on Ethereum – part 6: deploying a private testnet

When developing dapps and smart contracts, it’s of great importance to have a good development workflow and to go through the right amount of testing and validation. In previous posts I’ve explained how to setup a local blockchain node for testing. While the ultimate goal is to deploy your dapp to one of Ethereum’s test networks, and then the main network, it’s very useful to be able to run your own full private network. This can be used for internal testing, verification and simulation, within your own team or company. In this blog post we’ll cover just that — how to deploy a private testnet and the basic components involved.

Before we begin

If you haven’t already, I would recommend you to read the previous posts in this series.

Local vs private testnet

Running a local blockchain is different from a full scale private testnet in that it’s available over the Internet for others authorised parties to participate in. Such a private network is especially useful within a team or company to use as a staging or testing environment before deploying to a public Ethereum test network, like Rinkeby.

Overview

There are three main components of a simple private testnet (1) a bootnode (2) privileged mining nodes (3) monitoring services. In addition, one could also add Swarm nodes, blockchain explorer and a faucet, like on Ethereum’s Rinkeby testnet. In this post we will focus on the first three components that I would consider the bare minimum.

On the Ethereum mainnet mining is currently done with a proof-of-work algorithm. But testnets (both public and private) often use a different algorithm, because the available computational power is often significantly smaller, making the network susceptible to malicious attacks. The algorithm we will deploy for this private testnet is a proof-of-authority algorithm called Clique. Simply put, only a few privileged nodes will be allowed to mine (or seal) blocks in the blockchain.

A note on Puppeth

There’s a very useful utility called puppeth that ships with the Geth source code. Puppeth can be used to deploy and manage networks such as the one we are creating here. As of writing, Puppeth are making certain assumptions about your deployment, such as all servers being remote, or using Docker. Nevertheless, it’s a useful tool but for the purpose of this guide we will deploy everything more or less manually, because that’s more fun and educating :)

Server topology

One of the strengths of blockchain networks like Ethereum is how resilient they are, and how well they run on imperfect infrastructure. In practice, this means that where, on what or how you deploy the network doesn’t matter as much as with a LAMP stack, for example.

That said, server security is still important to keep private keys and certain configuration safe. However, I will keep server security such as filesystem permissions, firewall configuration out-of-scope for this blog post.

All services that we will go through can run out of separate directories on a single server, as separate servers, as Docker containers or anything else. I’ve documented everything with separate directory names etc., such that you have flexibility to choose. The topology we will use in this guide is simple and consists of three hypothetical Unix-like servers, running the below services as following:

  1. bootnode + one privileged node
  2. monitoring + one privileged node
  3. one privileged node

Deployment steps

1. Compile bootnode

So called bootnodes are used to help nodes in the network to initiate contact, like a meeting point on the Internet. After initial contact has been made nodes will talk directly to each other.

One implementation of Ethereum’s bootnode protocol is shipped with the Geth client, it’s simply called bootnode. For the purpose of this guide we will compile bootnode from source. Note that we assume that you have a working Go environment configured already. See part 4 of this series for a brief guide on setting up Go.

user@server1$ git clone https://github.com/ethereum/go-ethereum ~/go-ethereum
user@server1$ cd ~/go-ethereum
user@server1$ go install -v ./cmd/bootnode

2. Generate boot key, start bootnode

Next, we will generate a special key that basically will determine the bootnodes’s address, and start the bootnode itself.

user@server1$ bootnode -genkey ~/boot.key
user@server1$ bootnode -nodekey ~/boot.key

INFO [12-18|09:27:43] UDP listener up                          self=enode://03e6fdb0791e41be1f4acd219c574ba35ca033b1cbc5b6d6faeee046107db501d4ebcf2700a15fba79fc24d3fc211bd8142d82b1595c2c844fa68a2afe423063@[::]:30301
TRACE[12-18|09:28:07] >> PONG/v4                               addr=127.0.0.1:30303 err=nil
TRACE[12-18|09:28:07] << PING/v4                               addr=127.0.0.1:30303 err=nil

Important: take note of the enode address string above, and replace [::] with the server’s public IP address. Below I’ll refer to this public IP address as 1.1.1.1. You will need this when starting other nodes on the network.

3. Create accounts for all privileged nodes

It’s time to start setting up the three privileged mining nodes on the three separate servers. Here I assume that you already have geth installed and functioning. We will start by creating a separate data directory and new accounts for them all.

user@server1$ mkdir ~/data1
user@server1$ geth --datadir ~/data1 account new

Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat passphrase: 
Address: {8fff96ef79c64b4693c497f03ccab86ca312b0db}

Now, repeat the same on server2 and server3. For brevity, I’ll type out everything:

user@server2$ mkdir ~/data2
user@server2$ geth --datadir ~/data2 account new

Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat passphrase: 
Address: {1d3eaaeade1f26209bd612e18f4786f0ea983eff}
user@server3$ mkdir ~/data3
user@server3$ geth --datadir ~/data3 account new

Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat passphrase: 
Address: {201098c849e08993d0b739e1072f43f8063b8e6c}

Important: Take note of the addresses that you created for all three nodes. You will need to define these as privileged nodes when creating the genesis block for the new network.

4. Generate the genesis block

In order to generate the genesis block for this private network we will use a utility called Puppeth. As mentioned earlier, Puppet is very useful for managing networks such as the one we are creating. But for the purpose of this guide, we will only use Puppeth for generating the genesis file.

On server1 where we already checked out Geth, let’s compile Puppeth:

user@server1$ cd ~/go-ethereum
user@server1$ go install -v ./cmd/puppeth

Next, let’s go through the Puppeth wizard to create a new genesis file, called private.json in this example. You’ll need the addresses of the three privileged nodes handy. Outlined below, is the basic workflow to create a new genesis file with the three privileged addresses from earlier:

user@server1$ puppeth --network private

What would you like to do? (default = stats)
 1. Show network stats
 2. Configure new genesis
 3. Track new remote server
 4. Deploy network components
> 2

Which consensus engine to use? (default = clique)
 1. Ethash - proof-of-work
 2. Clique - proof-of-authority
> 2

How many seconds should blocks take? (default = 15)
> 

Which accounts are allowed to seal? (mandatory at least one)
> 0x8fff96ef79c64b4693c497f03ccab86ca312b0db
> 0x1d3eaaeade1f26209bd612e18f4786f0ea983eff
> 0x201098c849e08993d0b739e1072f43f8063b8e6c
> 0x

Which accounts should be pre-funded? (advisable at least one)
> 0x8fff96ef79c64b4693c497f03ccab86ca312b0db
> 0x1d3eaaeade1f26209bd612e18f4786f0ea983eff
> 0x201098c849e08993d0b739e1072f43f8063b8e6c
> 0x

Specify your chain/network ID if you want an explicit one (default = random)
> 

Anything fun to embed into the genesis block? (max 32 bytes)
> 

What would you like to do? (default = stats)
 1. Show network stats
 2. Manage existing genesis
 3. Track new remote server
 4. Deploy network components
> 2

 1. Modify existing fork rules
 2. Export genesis configuration
> 2

Which file to save the genesis into? (default = private.json)
> 

What would you like to do? (default = stats)
 1. Show network stats
 2. Manage existing genesis
 3. Track new remote server
 4. Deploy network components
> ^C

Note: You exit the wizard by pressing Ctrl+C at the end.

Before going further, we will make one minor adjustments to private.json (the genesis file) to allow larger transactions, such as complicated smart contracts. We need to change the gasLimit value to something like 0x8000000. A final example of the genesis file from above can be found here: https://gist.github.com/dickolsson/b6bfa37e2f82c7039ae770723a98e58f

Now, you need to make private.json available to any node that wants to participate in the network.

5. Initialise and start privileged nodes

The next step is to initialise and start all privileged nodes, pointing to the enode address and public IP number of the bootnode that we started above. Note that you’ll need to change the address values accordingly. And if you run all services on the same server, you’ll also need to change both --port and --rpcport to not collide

You need the private.json file (the genesis file) available on each server. When starting the node, you will be asked to type in the password to unlocked the base account for each node:

user@server1$ geth --datadir ~/data1 init ~/private.json
user@server1$ geth \
  --datadir ~/data1 \
  --rpc \
  --bootnodes "enode://03e6fdb0791e41be1f4acd219c574ba35ca033b1cbc5b6d6faeee046107db501d4ebcf2700a15fba79fc24d3fc211bd8142d82b1595c2c844fa68a2afe423063@1.1.1.1:30301"
  --mine \
  --etherbase 0x8fff96ef79c64b4693c497f03ccab86ca312b0db
  --unlock 0x8fff96ef79c64b4693c497f03ccab86ca312b0db

Now, repeat the same on server2 and server3, using the correct account addresses on each server. After all nodes have been started, you have the basis of your private Ethereum network running, congratulations!

6. Install monitoring services

In order to get an overview and monitor you newly deployed network we will use two services called eth-netstats and eth-net-intelligence-api. The former is a dashboard UI, and the latter is a API service that feeds blockchain data to the dashboard. In order to run these services you need Node, NPM and Grunt setup on your environment.

Let’s install everything on server2:

user@server2$ git clone https://github.com/cubedro/eth-net-intelligence-api ~/eth-netstats
user@server2$ cd ~/eth-netstats
user@server2$ npm install
user@server2$ grunt
user@server2$ git clone https://github.com/cubedro/eth-net-intelligence-api ~/eth-net-intelligence-api
user@server2$ cd ~/eth-net-intelligence-api
user@server2$ npm install

7. Start monitoring services

We will start the monitoring services using a minimal and default configuration. The services needs to share a secret key which we set up with a simple environment variable inline with each process for the purpose of this guide. To manage Node processes in production there are multiple options available, such as pm2. But we won’t cover that in this blog post.

Start the dashboard:

user@server2$ cd ~/eth-netstats
user@server2$ WS_SECRET=mysharedsecret npm start

In a separate tab on the same server, start the intelligence API:

user@server2$ ~/eth-net-intelligence-api
user@server2$ WS_SECRET=mysharedsecret npm start

You should now be able to visit http://publicaddress:3000 and see a dashboard similar to this:

Conslusion

You now have a very basic private Ethereum network running. It’s important to note that the above guide is a simple, minimal and not very secure deployment. Anyone on the Internet can potentially participate in your private network if they know your bootnode address and the genesis block you are using. Steps have to be taken to further lock down a private network such as the one we deployed here.