How to Build a Private Ethereum Blockchain
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.