How to Build a Private Ethereum Blockchain

Michael Wuehler
ConsenSys Media
7 min readMay 24, 2017

--

The following instructions walk through the installation of a single node private installation of an Ethereum blockchain using the Go-Ethereum client.

Prerequisite:
Ubuntu 16.04 Virtual Machine (for example from Azure, a Standard_A1 size should be fine for this tutorial)
Basic experience working on Linux command line

Step 1: Update Ubuntu installation with the latest packages

sudo apt-get update
sudo apt-get -y upgrade

Step 2: Install golang-go package

wget https://storage.googleapis.com/golang/go1.7.4.linux-amd64.tar.gz
sudo tar -xvf go1.7.4.linux-amd64.tar.gz
sudo mv go /usr/local
export GOROOT=/usr/local/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

Step 3: Build go-ethereum from github

git clone https://github.com/ethereum/go-ethereum.git ~/go-ethereum
cd ~/go-ethereum
git checkout master
make geth
cd ~

Step 4: Test geth installation

go-ethereum/build/bin/geth version

Expect a response similar to:

Geth
Version: 1.6.1-unstable
Git Commit: e353f9c0886f0cc13fd01c8b3abf2fa63025c62f
Architecture: amd64
Protocol Versions: [63 62]
Network Id: 1
Go Version: go1.7.5
Operating System: linux
GOPATH=
GOROOT=/usr/local/go

Step 5: Create an account to use as the coinbase for your blockchain node

An Ethereum account is the public key that stores ether that will be used in your private blockchain to pay for gas fees. Before we start the blockchain, we need to create an account that the mining rewards will be deposited too.

go-ethereum/build/bin/geth account new

You will be prompted to set a password for the account, DON’T FORGET YOUR PASSWORD, you will not be able to recover it later and won’t be able to spend the ether you mine or unlock that account. After entering the password twice, you should expect to get a response back like this:

Address: {941f1f0b08757457be0b52d83a1e3e566473ed61}

This is the public key of the Ethereum account. Ethereum convention is to prefix accounts with 0x so the account is sometimes seen as 0x941f1fobo8757457be0b52d83a1e3e566473ed61.

Step 6: Create JSON File for Genesis Block to Bootstrap Private Blockchain

Using a text editor, create a file CustomGenesis.json with the following contents:

{
"config": {
"chainId": 13,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"difficulty": "200000000",
"gasLimit": "2100000",
"alloc": {
"941f1f0b08757457be0b52d83a1e3e566473ed61": { "balance": "100000000" }
}
}

Step 7: Initialize the blockchain from your CustomGenesis.json file

go-ethereum/build/bin/geth init ./CustomGenesis.json

You should expect to see output similar to this:

INFO[02–23|20:20:03] Allotted 128MB cache and 1024 file handles to /home/ubuntu/.ethereum/geth/chaindata 
INFO[02–23|20:20:04] closed db:/home/ubuntu/.ethereum/geth/chaindata
INFO[02–23|20:20:04] Allotted 128MB cache and 1024 file handles to /home/ubuntu/.ethereum/geth/chaindata
INFO[02–23|20:20:04] successfully wrote genesis block and/or chain rule set: 5dd3be94dcbf5216aaa3e82700fb51a831257df5d45d984941a0a32ee0f960d8

Congratulations! You have initialized a private Ethereum blockchain!!

Before we move on, let’s take a break for some optional exploration of the files we have generated. In your home directory you will now see a hidden directory called .ethereum.

You will see two folders there, geth and keystore.

keystore contains the text files containing the Ethereum accounts you have available to the client. There should be just one files there corresponding to the account we created in Step 7. The file is a JSON-formatted ascii text and looks similar to this:

{“address”:”941f1f0b08757457be0b52d83a1e3e566473ed61",”crypto”:{“cipher”:”aes-128-ctr”,”ciphertext”:”16ad7e9df7036894a470520ed1d8aa241cbc1900c342f212
21acc62d548f0de0",”cipherparams”:{“iv”:”220c432b1cd2925576902a3c9afe026b”},”kdf”:”scrypt”,”kdfparams”:{“dklen”:32,”n”:262144,”p”:1,”r”:8,”salt”:”1cb8
e838e53980639df458b43c6bca4a5312abc27062e989a8d87d584efd0b3b”},”mac”:”598533d190ad4350e3b12cecc9220b77b7e1797b305294421503bbac5c5ec4d3"},”id”:”53ceeb
66-a6df-4064-bf8d-5689bd9ee98b”,”version”:3}

You’ll notice the public key is there in plaintext “address”:”941f1f0b08757457be0b52d83a1e3e566473ed61", the private key is encrypted in the file using the password you provided when creating the account.

geth contains one folder called chaindata. The chaindata folder contains the leveldb database files representing the blockchain genesis and subsequent blocks as they are mined and added to the blockchain. This folder will grow in size over time and if deleted or lost, the entire blockchain is gone and will need to be re-initialized.

If you want to back up your blockchain or move it to a new server, just copy the entire .ethereum folder and restore it to the home directory of the new server.

OK, let’s get back to our blockchain.

Step 8: Start up the blockchain!

go-ethereum/build/bin/geth \
--mine \
--nodiscover \
--maxpeers 0 \
--networkid 13 \
--rpc \
--rpccorsdomain "*"

Start geth with the following command line arguments

--mine Enable mining
--nodiscover Disables the peer discovery mechanism (manual peer addition)
--maxpeers value Maximum number of network peers (network disabled if set to 0) (default: 25)
--networkid value Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten) (default: 1)
--rpc Enable the HTTP-RPC server
--rpccorsdomain value Comma separated list of domains from which to accept cross origin requests (browser enforced)

You need to instruct your node to mine so that blocks are being created and so that when you begin to send transactions to the chain they will be processed and included in blocks. The nodiscover and maxpeers arguments are simply for security so that your chain implementation doesn’t attempt to discover any peers nor accept any peer requests. We set the value of the networkid argument to a random integer that isn’t 1, 2, 3 or 4 as those are reserved as identifiers for the public Ethereum networks. Finally, we enable rpc so your chain responds to the remote JSON-RPC API calls as is documented here: https://github.com/ethereum/wiki/wiki/JSON-RPC.

When your chain starts for the first time it will generate a DAG. For more details on what this means, visit https://github.com/ethereum/wiki/wiki/Ethash-DAG. This will only happen the first time you start your chain. The output will look similar to this:

INFO[02–23|21:57:54] Allotted 128MB cache and 1024 file handles to /home/ubuntu/.ethereum/geth/chaindata 
INFO[02–23|21:57:54] closed db:/home/ubuntu/.ethereum/geth/chaindata
INFO[02–23|21:57:54] instance:Geth/v1.6.0-unstable-11539030/linux/go1.6.2
INFO[02–23|21:57:54] Allotted 128MB cache and 1024 file handles to /home/ubuntu/.ethereum/geth/chaindata
INFO[02–23|21:57:54] Protocol Versions: [63 62], Network Id: 1999
INFO[02–23|21:57:54] Chain config:{ChainID: 0 Homestead: <nil> DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: <nil> EIP158: <nil>}
INFO[02–23|21:57:54] Last header: #227 [073961f2…] TD=31476282
INFO[02–23|21:57:54] Last block: #227 [073961f2…] TD=31476282
INFO[02–23|21:57:54] Fast block: #227 [073961f2…] TD=31476282
INFO[02–23|21:57:54] Starting Server
INFO[02–23|21:57:54] HTTP endpoint opened: http://localhost:8545
INFO[02–23|21:57:54] Listening on[::]:30303
INFO[02–23|21:57:54] Automatic pregeneration of ethash DAG ON (ethash dir: /home/ubuntu/.ethash)
INFO[02–23|21:57:54] IPC endpoint opened: /home/ubuntu/.ethereum/geth.ipc
INFO[02–23|21:57:54] Starting mining operation (CPU=1 TOT=2)
INFO[02–23|21:10:11] commit new work on block 1 with 0 txs & 0 uncles. Took 109.907µs
INFO[02–23|21:10:11] Generating DAG for epoch 0 (size 1073739904) (0000000000000000000000000000000000000000000000000000000000000000)
INFO[02–23|21:10:11] Listening on[::]:30303
INFO[02–23|21:10:11] Automatic pregeneration of ethash DAG ON (ethash dir: /home/ubuntu/.ethash)
INFO[02–23|21:10:11] IPC endpoint opened: /home/ubuntu/.ethereum/geth.ipc
INFO[02–23|21:10:11] checking DAG (ethash dir: /home/ubuntu/.ethash)
INFO[02–23|21:10:12] Generating DAG: 0%

You will also notice that mining starts and your node will begin generating blocks:

INFO[02–23|21:14:08] 🔨 mined potential block #1 [0ee888f0…], waiting for 5 blocks to confirm 
INFO[02–23|21:14:08] commit new work on block 2 with 0 txs & 0 uncles. Took 191.409µs

INFO[02–23|21:14:10] 🔨 mined potential block #2 [2179ec7e…], waiting for 5 blocks to confirm
INFO[02–23|21:14:10] commit new work on block 3 with 0 txs & 0 uncles. Took 161.908µs

INFO[02–23|21:14:10] 🔨 mined potential block #3 [08cb41c7…], waiting for 5 blocks to confirm
INFO[02–23|21:14:10] commit new work on block 4 with 0 txs & 0 uncles. Took 216.711µs

INFO[02–23|21:14:11] 🔨 mined potential block #4 [d7a60ebd…], waiting for 5 blocks to confirm
INFO[02–23|21:14:11] commit new work on block 5 with 0 txs & 0 uncles. Took 781.739µs

INFO[02–23|21:14:18] 🔨 mined potential block #5 [eb021a9c…], waiting for 5 blocks to confirm
INFO[02–23|21:14:18] commit new work on block 6 with 0 txs & 0 uncles. Took 163.508µs

INFO[02–23|21:14:26] 🔗 mined block #1 [0ee888f0…] reached canonical chain
INFO[02–23|21:14:26] 🔨 mined potential block #6 [e901a53c…], waiting for 5 blocks to confirm

That’s it! Your blockchain is now running, processing blocks and listening to remote RPC calls so that you and your code can interact with it. In fact let’s try a few tests using the JSON-RPC API documented here: https://github.com/ethereum/wiki/wiki/JSON-RPC. All of the methods documented here can be executed against your running blockchain. Leave your chain running and open a second window with a shell to your VM.

Now, execute this command from the command line:

curl -X POST --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":67}' http://localhost:8545

You should receive a response like this:

{"jsonrpc":"2.0","id":67,"result":"Geth/v1.6.0-unstable-11539030/linux/go1.6.2"}

Now check the balance in ether of the Ethereum account that your blockchain is mining to:

curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x941f1f0b08757457be0b52d83a1e3e566473ed61", "latest"],"id":1}' http://localhost:8545

You should receive a response like this:

{"jsonrpc":"2.0","id":1,"result":"0x3ee23bde0e7d200000"}

The result is returned in hexadecimal, which when converted to decimal is: 1160000000000000000000. As noted in the documentation for the eth_getBalance method, the value returned is in wei, which is the smallest denomination of ether. Using a converter like this one, you can convert that value in wei to ether to see you have 1160 ether in your account. If only it were real ether! This ether only has value on this private network.

From here you can start to experiment more with the methods in the API documentation and you will start to see how easy it is to interact with your running blockchain.

Adding Additional Nodes

Now you have your single node private blockchain. To add a second node, follow the steps above to build your second node.

Next we will make some minor modifications to the start script to enable the nodes to communicate with each other.

First, we need to update the start script to allow maxpeers 1

go-ethereum/build/bin/geth \
— mine \
— nodiscover \
— maxpeers 1 \
— networkid 13 \
— rpc \
— rpccorsdomain "*"

Next, since we are still using the --nodiscover option, we need to explicitly instruct each node to peer with the other at startup.

Each node has it’s unique identifier, called an enode. The enode will be visible in the logs at startup, you will see something similar to this in the logs:

enode://98267578205c68fc09e917e67606d8cd6fefd3be84a17c26a9840af003fc8659bfdaf77128f22216aeeeee8d3ddd3470297d9a8b8c9581d31560f1a2b0964334@[::]:30303

We will leverage the command line option to hardcode the enode for node A to node B and vice versa.

--bootnodes value Comma separated enode URLs for P2P discovery bootstrap

The only change we need to make in the above enode is to fill in the IP address of the node where it can be accessed on port 30303, which is the default port for P2P networking. Our start script becomes something similar to the following, where the “enode” is replaced with the corresponding correct value.

go-ethereum/build/bin/geth \
— mine \
— nodiscover \
— maxpeers 1 \
— networkid 13 \
— rpc \
— rpccorsdomain "*" \
— bootnodes 98267578205c68fc09e917e67606d8cd6fefd3be84a17c26a9840af003fc8659bfdaf77128f22216aeeeee8d3ddd3470297d9a8b8c9581d31560f1a2b0964334@[IP ADDRESS OF OTHER NODE]:30303

After starting each instance, you will be able to check that they are peering using the JSON-RPC method call.

That’s it, from here you can continue to play with your new chain and experiment with the JSON-RPC API and other start flags.

--

--