Setting up a private two node Ethereum Network using Geth 1.8.x on a Mac 10.13.x

richard muth
6 min readMar 28, 2017

--

The following describes the steps in creating a private two node Ethereum Network that uses static clustering; and then testing by sending Ether from an enode1 account to an enode2 account.

An overview of the steps

  1. Download Geth and tools, version 1.8.2 on Mac 10.13.1
  2. Create a directory structure to isolate each enodes chain data, keys, etc.
  3. Use Geth to create new Ethereum accounts on each enode
  4. Use Geth to create the private blockchain genesis block and initialize the chain data.
  5. Start the enodes unclustered to get the enode URLs needed to cluster the nodes. Nodes are started with ‘— nodiscover’.
  6. Configure the cluster so the enodes knows about each other, this example uses the static nodes file.
  7. Use the Geth console to transfer Ether from an enode1 account to an enode2 account, and then check the transaction on both nodes to ensure all synchronized.

1. Downloaded Geth and All tools

https://geth.ethereum.org/downloads/

2. Create the enode data directory structure

To isolate each enodes chain data, keys, and etc, create the following the directory structure that will be used for the ‘ — datadir’ and ‘ — ipcpath

  • (dpath)/pn/node1
  • (dpath)/pn/node2

3. Create new Ethereum accounts

Create the following new accounts as shown below. See Ethereum wiki for more details on creating accounts.

  • account1_1 used for enode1 coinbase
  • account1_2 used as another enode1 account
  • account2_1 used as enode2 coinbase
  • account2_2 used as another enode2 account
// enode1 accounts coinbase and then another
geth --datadir (dpath)/pn/node1 account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {f628e41f3ca68341e9857102d4b3040aa418d763} //account1_1
geth --datadir (dpath)/pn/node1 account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {fa4ed7b6d7a8e0140f4dd2b54de96a3daf4fdeca} //account1_2
// enode2 accounts coinbase and then another
geth --datadir (dpath)/pn/node2 account new

Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {9b71506979e6f19b0fa0040110163a68efa20f58} //account2_1
geth --datadir (dpath)/pn/node2 account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {221ac5243c1015f44a31ff3040ad2df1413db7c4} //account2_2

4. Create the Genesis JSON file, and initialize the new blockchain.

Below is the genesis.json with the following properties updated from the example on Ethereum wiki.

  • config.chainId change to a non zero value as if left as zero then send transaction always failed with insufficient funds even when there was some, see link. (Did not have time to look at why)
  • nonce change to a new value for this network, so that if other parties trying to join with a different genesis block nonce value are rejected.
  • coinbase left as the default but change when start the the Geth enode using a command line flag to set to the relevant new account.
  • alloc left as the default empty as will get all Ether from mining. Could create some test accounts and pre-allocated but not needed for this use case.
{
"config": {
"chainId": 1234,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc": {},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x20000",
"extraData" : "",
"gasLimit" : "0x2fefd8",
"nonce" : "0x1010300100000142",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00"
}

4.1 Initialize each nodes blockchain using the Genesis block

Initialize every Geth node with the above genesis.json prior to starting it up to ensure all blockchain parameters are correctly set for each node. This is done once.

// initialize node1 creates the geth/chaindata and keystore folders
$geth init (dpath)/pn/pn_genesis.json --datadir=(dpath)/pn/node1
// initialize node2 creates the geth/chaindata and keystore folders
$geth init (dpath)/pn/pn_genesis.json --datadir=(dpath)/pn/node2.

5. Running the Geth enodes

The Geth enode1 and enode2 are started with the commands shown. The following command line flags are used, see Ethereum wiki for more details on flags.

Ethereum options

  • — datadir value different for each enode so isolate data , (dpath)/pn/node1
  • — networkid value used to pass the Network identifier, set to the same value on each enode.

API and Console options

  • — rpc enable the HTTP-RPC server
  • — rpcaddr 127.0.0.1 set the host as local
  • — rpcport value different for each enode
  • — rpcapi “eth,net,web3” set to specific values as opposed to default
  • — ipcpatch value different for each enode

Miner options

  • — mine also act as miner, with one thread
  • — gasprice value set the price for mining a block low
  • — etherbase set to account1_1 and account2_1 accordingly

Network options

  • — port value different for each enode
  • — nodiscover to stop other nodes trying to connect

Logging options

  • — verbosity 3 set as opposed to default

The commands to start the Geth enode1 and enode2

// ENODE1 port 30304, rpcport 8504, ipc node1/geth.ipcpatch
geth --datadir ../../private-network/node1 \
--networkid 2061 \
--port 30304 \
--nodiscover \
--rpc \
--rpcaddr 127.0.0.1 \
--rpcport 8504 \
--ipcpath ../../private-network/node1/geth.ipc \
--mine --minerthreads 1 --gasprice "10000" \
--etherbase 0xf628e41f3ca68341e9857102d4b3040aa418d763 \
--verbosity 3
// ENODE2 on port 30305, rpcport 8505, ipc node2/geth.ipcpatch
geth --datadir ../../private-network/node2 \
--networkid 2061 \
--port 30305 \
--nodiscover \
--rpc \
--rpcaddr 127.0.0.1 \
--rpcport 8505 \
--ipcpath ../../private-network/node2/geth.ipc \
--mine --minerthreads 1 --gasprice "10000" \
--etherbase 0x9b71506979e6f19b0fa0040110163a68efa20f58 \
--verbosity 3

5.1 Stopping the enodes

I stop the enode uses kill -INT (pid) as this releases all the ports etc.

ps aux | grep geth
kill -INT (pid)

5.2 Attaching to the enodes JavaScript Console

Once running one can attach the Geth nodes

// Attach to enode1 using ipc
geth attach ipc:(dpath)/pn/node1/geth.ipc
// Attach to enode2 using ipc
geth attach ipc:(dpath)/pn/node1/geth.ipc

6. Configuring the cluster

I decided to configure the cluster manually using static nodes and disable discovery by running the enodes with the ‘ — nodiscover’ command line flag, see Ethereum wiki. My main reasons were (1) privacy as felt I had more control over what enodes could connect even though the genesis block nonce stops invalid enodes connecting as there genesis block has a different nonce, and (2) simplicity as less moving parts i.e. no bootnode.

Step-1 get each enode’s URL, as shown below, note you will need to replace the [::] with the local host 127.0.0.1.

// Attach to enode1 using ipc
geth attach ipc:(dpath)/pn/node1/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/v1.8.2-stable-b8b9f7f4/darwin-amd64/go1.9.4
coinbase: 0xf628e41f3ca68341e9857102d4b3040aa418d763
at block: 1306 (Mon, 26 Mar 2018 14:46:50 PDT)
datadir: (dpath)/pn/node1
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
// find the enode1 URL
> admin.nodeInfo.enode
"enode://6e15294e43e3f64b4f9ca57df7034acd7e5635043c3581d9661525818a36e8859e846f2cd81f2d244c421614d1424f15674874b3daa682685c05455fce75c89c@[::]:30304?discport=0"
> exit
// Attach to enode2 using ipc
geth attach ipc:(dpath)/pn/node2/geth.ipc
Welcome to the Geth JavaScript console!
...
> admin.nodeInfo.enode
"enode://04f7c9d39b98b3dfa7c3d5adafa3862c90c1c425f7bfd2059b594aeada97eb87b3cd98ced160b77f2a1ade4bfada94c492137386247ce6b77d5eebd5f2c823cd@[::]:30305?discport=0"
> exit

Step-2 create the static-nodes.json as shown below and put an instance in each of the enodes “(dpath)/pn/nodeX/geth” directory. I could have used admin.addPeer(enodeURL) but wanted to configure.

// the static-nodes.json file
[
"enode://6e15294e43e3f64b4f9ca57df7034acd7e5635043c3581d9661525818a36e8859e846f2cd81f2d244c421614d1424f15674874b3daa682685c05455fce75c89c@127.0.0.1:30304?discport=0",
"enode://04f7c9d39b98b3dfa7c3d5adafa3862c90c1c425f7bfd2059b594aeada97eb87b3cd98ced160b77f2a1ade4bfada94c492137386247ce6b77d5eebd5f2c823cd@127.0.0.1:30305?discport=0"
]

Step-3 stop and restart the Geth enodes as described in 5. Once started can examine enode as shown below

// Attach to enode1 using ipc
geth attach ipc:(dpath)/pn/node1/geth.ipc
Welcome to the Geth JavaScript console!
...
> net.peerCount
1
// peer has the id of enode2
> admin.peers
[{
caps: ["eth/62", "eth/63"],
id: "04f7c9d39b98b3dfa7c3d5adafa3862c90c1c425f7bfd2059b594aeada97eb87b3cd98ced160b77f2a1ade4bfada94c492137386247ce6b77d5eebd5f2c823cd",
name: "Geth/v1.8.2-stable-b8b9f7f4/darwin-amd64/go1.9.4",
network: {...remove for clarity...},
protocols:{...remove for clarity}
}]

7. Transfer Ether from an enode1 account to an enode2 account

Step-1 check enode1 Accounts and Ether

// Attach to enode1
geth attach ipc:(dpath)/pn/node1/geth.ipc
Welcome to the Geth JavaScript console!
...
> eth.accounts
["0xf628e41f3ca68341e9857102d4b3040aa418d763", "0xfa4ed7b6d7a8e0140f4dd2b54de96a3daf4fdeca"]
> eth.coinbase
"0xf628e41f3ca68341e9857102d4b3040aa418d763"
> web3.fromWei(eth.getBalance(eth.coinbase),"ether")
252.34375
> web3.fromWei(eth.getBalance( "0xfa4ed7b6d7a8e0140f4dd2b54de96a3daf4fdeca"),"ether")
0

Step-2 check enode2 Accounts and Ether

// Attach to enode2
geth attach ipc:(dpath)/pn/node2/geth.ipc
Welcome to the Geth JavaScript console!
...
> eth.accounts
["0x9b71506979e6f19b0fa0040110163a68efa20f58", "0x221ac5243c1015f44a31ff3040ad2df1413db7c4"]
> eth.coinbase
"0x9b71506979e6f19b0fa0040110163a68efa20f58"
> web3.fromWei(eth.getBalance(eth.coinbase),"ether")
172.96875
> web3.fromWei(eth.getBalance( "0x221ac5243c1015f44a31ff3040ad2df1413db7c4"), "ether")
2

Step-3 attach to enode1 then (1) unlock coinbase (account1_1); (2) send Ether to enode2 account2_2; (3) check account2_2 balance

// Attach to enode1
geth attach ipc:(dpath)/pn/node1/geth.ipc
Welcome to the Geth JavaScript console!
...
> personal.unlockAccount(eth.coinbase)
Unlock account 0xf628e41f3ca68341e9857102d4b3040aa418d763
Passphrase:
true
> eth.sendTransaction({from:eth.coinbase, to:"0x221ac5243c1015f44a31ff3040ad2df1413db7c4", value: web3.toWei(2, "ether") , gas: 40000 });
"0x5662adf7e7af644540cdc04f13277d8650de23adf6be8292f9d5fd12c05679fc"
> web3.fromWei(eth.getBalance( "0x221ac5243c1015f44a31ff3040ad2df1413db7c4"),"ether")
2
>eth.

Step-4 attach to enode2 then check account2_2 balance which should be the same value.

// Attach to enode2
geth attach ipc:(dpath)/pn/node2/geth.ipc
Welcome to the Geth JavaScript console!
...
> web3.fromWei(eth.getBalance( "0x221ac5243c1015f44a31ff3040ad2df1413db7c4"),"ether")
2

Step-Interest looking at the transaction from any enode

>eth.getTransaction( "0x5662adf7e7af644540cdc04f13277d8650de23adf6be8292f9d5fd12c05679fc")
{
blockHash: "0x91eba0db6f3fec5586ad0b48cc046698a8e5e64485d34a429aed2b3fb978a311",
blockNumber: 136,
from: "0xf628e41f3ca68341e9857102d4b3040aa418d763",
gas: 40000,
gasPrice: 10000,
hash: "0x5662adf7e7af644540cdc04f13277d8650de23adf6be8292f9d5fd12c05679fc",
input: "0x",
nonce: 0,
r: "0xfcc8e43d2636088030b139c214b86dc5e8e3999f6159a98729cce017d201e24d",
s: "0x70b9f283c86bc7697a0737b55d5dccb94973e1a349723a6090eb805c01a57127",
to: "0x221ac5243c1015f44a31ff3040ad2df1413db7c4",
transactionIndex: 0,
v: "0x9c8",
value: 2000000000000000000
}

Done :) My next steps are to use Docker, and then move into an AWS environment. So more updates to follow.

--

--