Setup a Ethereum private network
There are many tutorials online on this topic, but I found most of them not touching on the essence of the problem, and some of them are simply outdated. So while risking to be another outdated post, I want to share my experience of getting a three-node ETH network working (one is my laptop, one is AWS EC2, and the final one is a digital ocean instance).
Our network topology
We will setup a bootnode, which simply maintains the network peer list for us, without actual mining or verifying transactions. And we will have three clients, and one of them is a miner. They will first connect to bootnode, and then connect with each other. Note that one node can be a client node and a bootnode at the same time, as long as the ports don’t collide with each other (so in fact you can setup many nodes in localhost
as well, but for testing purpose, I used three remote nodes).
Preparation
Configuration file: we need only a genesis.json
template file beforehand. It specifies the genesis block, which determines the parameters of our ETH blockchain. We will fill it with preallocation parameters later. Note that only the initial chain generated from the same genesis.json
would be compatible with each other.
Installation: see https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum
Setup the bootnode
The bootnode does NOT depend on the genesis block or whatever. It can be compatible with all ETH clients. The only thing you have to do:
bootnode --genkey=boot.key
# run this in background, note the enode:// address it prints
bootnode --nodekey=boot.key
Run clients
For each client, first create account locally using geth account new
, which is essentially just a passphrase-protected asymmetrical key pair. The public key is the ETH address, and will compose the node address as well, while the private key is stored locally.
Assume that we create three accounts with addresses:
238dd521ad221b37cc176fa9f4bf88cf19fe39f1
aebc7588345fc7963505dd6de9d12390980fc13d
a5c77bd6319a5eaba9494acd90cac9712f9e15c9
Then, we will create a genesis.json
which preallocates some ethers for these accounts for testing purpose, so we can conduct transactions from the very beginning:
{
"config": {
"chainId": 1337,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"nonce": "0x0000000000000042",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x00",
"gasLimit": "0x8000000",
"difficulty": "0x01",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x238dd521ad221b37cc176fa9f4bf88cf19fe39f1",
"alloc": {
"0x238dd521ad221b37cc176fa9f4bf88cf19fe39f1" : {
"balance" : "200000000000000000000000"
},
"0xaebc7588345fc7963505dd6de9d12390980fc13d" : {
"balance" : "10000000000000000000"
},
"0xa5c77bd6319a5eaba9494acd90cac9712f9e15c9" : {
"balance" : "20000000000000000000"
}
}
}
Then we initialize the chain data from the genesis.json
. This is done locally and doesn’t depend on the bootnode:
geth --datadir .datadir init genesis.json
Note that we use a customized path for chaindata instead of the default ~/.ethereum
, so as to avoid collision with the public net.
After that, we will connect the client with the bootnode, which in turn helps connect with more peer nodes.
geth --datadir .datadir --networkid 1000 --rpc --ipcpath .datadir/geth.ipc --bootnodes <ADDR> console
The <ADDR>
is the address printed by bootnode daemon above, with [::]
replaced by the node’s IP.
After you entered the console, you can start finding the peers using admin.peers
, e.g.
> admin.peers
[{
caps: ["eth/63"],
id: "69ef2cbb9d381cb57e7978b17d062577950ed192152314ac17ab0bf7fe8e28c3e7bd13c7d220ff2380845b09fdd738967bf73e0f469408a9e8c6d5f01a4f7e7e",
name: "Geth/v1.6.7-stable-ab5646c5/linux-amd64/go1.8.1",
network: {
localAddress: "45.55.18.182:30303",
remoteAddress: "52.14.146.67:41032"
},
protocols: {
eth: {
difficulty: 1,
head: "0x5478e39ca8247b0d557254e2c6a84d2b0311370623f716909445d1f39e5045a4",
version: 63
}
}
}, {
caps: ["eth/63"],
id: "f0999401f838d9d1b0776dd5781ea3ba5fa64537a527d872667784296585d79a3716ad1ccb8b0eae46b546587fcbef599a1804271ca40c8378527feb70b6b3f1",
name: "Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.9",
network: {
localAddress: "45.55.18.182:30303",
remoteAddress: "60.10.118.236:50466"
},
protocols: {
eth: {
difficulty: 1,
head: "0x5478e39ca8247b0d557254e2c6a84d2b0311370623f716909445d1f39e5045a4",
version: 63
}
}
}]
Or querying balance e.g.:
> eth.getBalance("0xc8d11d64b09853c22ad9c917ecc6930164373e97")
2e+23
Or sending transactions e.g.:
> personal.unlockAccount(eth.coinbase, "password")
> eth.sendTransaction({from:eth.coinbase, to:eth.accounts[1], value: web3.toWei(0.05, "ether")})