This tutorial is part of a series of articles introduced here.
In part 3, we configured the miners of our private blockchain.
NB: It is important that what we mean by “private Ethereum blockchain” in this context has absolutely nothing to do with the “private blockchain” championed by Hyperledger, Eris/Monax, or the recently announced Enterprise Ethereum Alliance. These are different codebases with different client applications, so they correspond to different implementations of a different set of concepts. They are private in the sense that they limit who has access to their chain. They are also called permissioned blockchains, and to be perfectly transparent, we wish they were not even called blockchains at all. In this series of articles, what we call “private blockchain” is a private instance of the Ethereum implementation. Same code base, same client application, different network identifier and genesis block. In other words, what we will come to create in this series is a chain with the same rules as the main chain, the same consensus algorithm too, but a different root block. This will become clearer in part 3. For more information about the 3-layer model and differences between concepts, implementations and instances, you can also watch our Devoxx talk and read this article.
We saw that transactions are not propagated from miner #1 to miner #2.
Remember that a blockchain is a peer-to-peer network. This means that our miners need to see each other in order to propagate transactions.
In addition, the discovery protocol is not working on a private blockchain. This means that we have to configure each node to specify the identity and the location of its peers.
This part describes how to pair our miners.
Let’s start by retrieving the node information that uniquely identify each node deployed in the blockchain.
Step 1 Clean your miners
We noticed some issues when we tried to synchronise our miners having several thousand blocks. Sometimes, miners were not able to pair or the synchronization process became too slow.
The easiest way is to reset the chaindata of the private blockchain installed on each miner.
Step 1.1 – Stop miners
First ensure that your miners are stopped.
As a reminder, you can either press ^C on the miner’s console, or search and kill the “geth” process:
computer$ ps aux | grep geth eloudsa 43872 0.0 2.2 556717632 363492 s001 S+ 3:49PM 1:58.01 geth --identity miner1 --dev computer$ kill -INT 43872
Step 1.2 – Delete the chaindata
Delete chain data for miners:
computer$ rm -rf ~/ChainSkills/miner1/geth computer$ rm -rf ~/ChainSkills/miner2/geth
Step 1.3 – Initialize miners
We initialize our miners with the genesis block.
Start with the miner #1:
computer$ cd ~/ChainSkills computer$ geth --datadir ~/ChainSkills/miner1 init genesis.json
Then the miner #2:
computer$ cd ~/ChainSkills computer$ geth --datadir ~/ChainSkills/miner2 init genesis.json
Step 2 Get IP address
Get the IP address of your computer running miners.
computer$ ipconfig getifaddr en0 192.168.1.39
Replace the interface according to your network settings:
- en0: wired/ethernet
- en1: wireless
Step 3 Get Node info from miner #1
Step 3.1 Node info for miner #1
Let’s start the miner #1:
computer$ cd ~/ChainSkills/miner1 computer$ ./startminer1.sh ...
computer$ geth attach ... >
Stop the mining to avoid generating too much blocks before the synchronisation process:
> miner.stop() true
Retrieve the node information:
> admin.nodeInfo.enode "enode://b8863bf7c8bb13c3afc459d5bf6e664ed4200f50b86aebf5c70d205d32dd77cf2a888b8adf4a8e55ab13e8ab5ad7ec93b7027e73ca70f87af5b425197712d272@[::]:30303?discport=0"
Step 3.2 Get Node info from miner #2
computer$ cd ~/ChainSkills/miner2 computer$ ./startminer2.sh ...
computer$ geth attach ipc:./miner2/geth.ipc ... >
Stop the mining to avoid generating too much blocks before the synchronisation process:
> miner.stop() true
Retrieve the node information:
> admin.nodeInfo.enode "enode://41be9d79ebe23b59f21cbaf5b584bec5760d448ff6f57ca65ada89c36e7a05f20d9cfdd091b81464b9c2f0601555c29c3d2d88c9b8ab39b05c0e505dc297ebb7@[::]:30304?discport=0"
Step 4 – Pair the nodes
There are different ways to pair nodes.
Here, we illustrate how to define permanent static nodes stored in a file called “static-nodes.json“. This file will contain the node information of our miners.
Based on our environment, we will have the following content:
[ "enode://b8863bf7c8bb13c3afc459d5bf6e664ed4200f50b86aebf5c70d205d32dd77cf2a888b8adf4a8e55ab13e8ab5ad7ec93b7027e73ca70f87af5b425197712d272@192.168.1.39:30303", "enode://41be9d79ebe23b59f21cbaf5b584bec5760d448ff6f57ca65ada89c36e7a05f20d9cfdd091b81464b9c2f0601555c29c3d2d88c9b8ab39b05c0e505dc297ebb7@192.168.1.39:30304" ]
You will notice that we have replaced the placeholder [::] with the IP address of our computer. The last part (?discport=0) has been removed.
Each node information is separated by a comma character.
Based on our example, the file “static-nodes.json” must be stored under the following location:
- ~/ChainSkills/miner1
- ~/ChainSkills/miner2
When the miner starts, the Geth process will process the file automatically.
Step 5 – Restart your miners
Stop and start the miners to ensure that they will properly reload the “static-nodes.json” file.
If you check the console of your miners, you should see a line mentioning the synchronisation process (“Block synchronisation started”):
... I1219 01:12:03.223537 eth/downloader/downloader.go:326] Block synchronisation started ...
Step 6 – Check the synchronisation process
We check if the miners are properly paired.
Step 6.1 – Check from miner #1
Open the Geth console linked to the miner #1:
computer$ geth attach ... >
Check which nodes are paired to miner #1:
> admin.peers [{ caps: ["eth/62", "eth/63"], id: "41be9d79ebe23b59f21cbaf5b584bec5760d448ff6f57ca65ada89c36e7a05f20d9cfdd091b81464b9c2f0601555c29c3d2d88c9b8ab39b05c0e505dc297ebb7", name: "Geth/miner2/v1.5.5-stable-ff07d548/darwin/go1.7.4", network: { localAddress: "192.168.1.39:59153", remoteAddress: "192.168.1.39:30304" }, protocols: { eth: { difficulty: 96831214, head: "0x0f8a3318a47429aee7a442cd1c3258eab7427b7fa1cb3c2a3e4bdf70ed6d8cf8", version: 63 } } }]
We can see that our node is paired to miner #2 identified by its IP address and its port number (30304).
Step 6.2 – Check from miner #2
Open the Geth console linked to the miner #2:
computer$ geth attach ipc:./miner2/geth.ipc ... >
Check which nodes are paired to miner #2:
> admin.peers [{ caps: ["eth/62", "eth/63"], id: "b8863bf7c8bb13c3afc459d5bf6e664ed4200f50b86aebf5c70d205d32dd77cf2a888b8adf4a8e55ab13e8ab5ad7ec93b7027e73ca70f87af5b425197712d272", name: "Geth/miner1/v1.5.5-stable-ff07d548/darwin/go1.7.4", network: { localAddress: "192.168.1.39:30304", remoteAddress: "192.168.1.39:59153" }, protocols: { eth: { difficulty: 88045328, head: "0xa084d0c7f1a18120780c44bd4ba6c3ec237eb3feb0912bffd74ac1030246a723", version: 63 } } }]
We can see that our node is paired to miner #1 identified by its IP address and its port number (59153).
Step 7 -Validate the synchronisation
Let’s validate the synchronisation process by sending some ethers between accounts defined on each miner.
We are going to send 10 ethers between the following accounts:
- miner #1(eth.coinbase) -> miner #2(eth.accounts[1])
eth.coinbase is the default account that receive the rewards for the mining process. In the miner #1, eth.coinbase is the same as eth.accounts[0].
First, make sure that the mining process is running on both miners.
Step 7.1 – Send ethers from Miner #1 to Miner #2
Start a Geth console linked to the miner #2, retrieve the address of the account[1] and check its initial balance:
computer$ geth attach ipc:./miner2/geth.ipc ... > eth.accounts[1] "0xfa919b49ef34a821fb4cadfdfa5cc6593cb46fe1" > web3.fromWei(eth.getBalance(eth.accounts[1])) 0
The account #1 has no ether.
From the Geth console linked to the miner #1, send 10 ethers from the default account to the address of the account[1]:
computer$ geth attach ... > eth.sendTransaction({from: eth.coinbase, to: "0xfa919b49ef34a821fb4cadfdfa5cc6593cb46fe1", value: web3.toWei(10, "ether")})
From the miner #1, check if the recipient has received the ethers:
> web3.fromWei( eth.getBalance("0xfa919b49ef34a821fb4cadfdfa5cc6593cb46fe1")) 10
From the miner #2, check that you have the same balance:
> web3.fromWei( eth.getBalance(eth.accounts[1])) 10
Step 7.2 – Send ethers from Miner #2 to Miner #1
We are going to send 2 ethers between the following accounts:
- miner #2(eth.accounts[1]) -> miner #1(eth.accounts[1])
From the Geth console linked to the miner #1, check the initial balance of the account that will receive ethers:
computer$ geth attach ... > eth.accounts[1] "0xae3ab39b3ebc425289dad620aece197a4a3f8940" > web3.fromWei(eth.getBalance(eth.accounts[1])) 0
The account #1 has 0 ether.
From the Geth console linked to the miner #2, send 2 ethers from the account #1:
computer$ geth attach ipc:./miner2/geth.ipc ... > eth.sendTransaction({from: eth.accounts[1], to: "0xae3ab39b3ebc425289dad620aece197a4a3f8940", value: web3.toWei(2, "ether")}) Error: account is locked at web3.js:3119:20 at web3.js:6023:15 at web3.js:4995:36 at <anonymous>:1:1
> personal.unlockAccount(eth.accounts[1], 'type your password') true
From the miner #2, we are ready to send our transaction:
> eth.sendTransaction({from: eth.accounts[1], to: "0xae3ab39b3ebc425289dad620aece197a4a3f8940", value: web3.toWei(2, "ether")}) "0x02b1c360e3b094e8c7faac3d3f8def8eec6350a3d68a1b7e071827d1026221f0"
From the miner #2, check that our balance has changed (2 ethers plus some transaction fees):
> web3.fromWei( eth.getBalance(eth.accounts[1])) 7.99958
From the miner #1, check that the recipient has received the ethers:
> web3.fromWei( eth.getBalance(eth.accounts[1])) 2
Summary
Congratulations! You have synchronised your miners.
In part 5, we will synchronise the RPi node with these miners.
Shameless plug
While you are waiting for the next part of this series, we just wanted to let you know that we are currently preparing a full-blown online training about the development of distributed applications on Ethereum and we are looking for your feedback to figure out what you would like to see in this training. If you want to help us, you can take a few minutes to answer a survey here.
And if you just want us to keep you informed when the full online training program will be available, you can register to our mailing list on beta.chainskills.com.
Hi Said,
Many thanks for the tutorial, very helpful indeed 🙂
However I ran into a problem when I trie to sync up the two miners. Neither static-nodes.json nor admin.addPeer works. My miners just won’t find each other :/
Any idea why? I strictly followed your tutorial and was able to successfully finish most of the steps before this point.
Thanks,
Billie
Hi Billie
Can you please share your static-nodes.json file?
My static-nodes.json file is the following:
[
“enode://99ad3eb7d00fc1acec8a347961b5f2447cfeab93d2f7c9ce070d357eb300f4bd213411f2e28fe277bb43163790bd8fd7dffb6c279757fc674959b1c8c91afccc@192.168.1.39:30303”,
“enode://192bbf6a845c3c996ddf3cbdc1eac7a8468c5ad8c4c05a5b970e4fea85e0867a9fab843e4ae0b93e360a97227c2b1c141a3bd196c4ad4b00567b1e07e1d9c2b7@192.168.1.39:30304”
]
You will notice the comma character between each enode information.
If this comma character is missing, the pairing will not work.
Cheers
Said
Hi Said,
Thanks for getting back to me.
I have the comma in between the two nodes, please see the code below:
[
“enode://d3773bd98b93179c0512b77d65733ed18e4922522d468b6595398cec44604cd50b6b009f01148fc4d6a746c7c596be2ac8f067f77bb55b5bba017e307b1894d0@127.0.0.1:30303”,
“enode://f21f1bbe03d60dc144acd4662e16b2b6ba2d3b6de6c3949b73957b39cebcc002380bce624ec5d7e5f72e2560590e1b3adec98a6ed4938f95b19f924ad982f6c5@127.0.0.1:30304”
]
I used both my external IP and the localhost IP and neither of these worked.
Also do you know why admin.addPeer() doesn’t work either?
Many thanks,
Billie
If you don’t mind me asking another question.
How does it work when two miners trying to sync with each other when they already have their own chain? When I started mining, they were building their own blocks already, so then how do they sync with each other?
After I’ve placed the static-nodes.json file in both the data directoies, I started the two miners again, but I always get the same warning: blockchain not empty, fast synch diabled.
I’m not sure if it has anything to do with the static-nodes.json file?
Thanks a lot,
Billie
Interesting questions!
First, the miner that has the longest chain will be the reference. The second miner will rebuild its chain according the reference one. At the end of this process, both chains will be synchronised.
About your second question, don’t be surprised regarding the warning. The fast synchronisation is disabled while the synchronisation process is running between both chains.
Btw, have you been able to sync your miners?
Hi Said,
I managed to! I’ve realized that you need to make sure both the instances are running while you use admin.adPeer. If only one is running it won’t be able to find it.
Also for people out there who has managed to connect the two nodes using static-nodes.json bu couldn’t find them using admin.peers, you need to again make sure both nodes are running. If one is down, even if they have been connected previously admin.peers will still return an empty array.
Cheers,
Billie
Hi Billie
You’re right, miners have to be started before the pairing process. This is already mentioned in the step: Step 5 – Restart your miners
Cheers
Said
Hi,
I enjoyed the series of articles very much. It helped me a lot in creating two nodes in sync and mining.
I do have following questions, it would be extremely helpful if you can answer them:
1. The process mentioned in the 4th article of the series “pair the miners…”, you have used “static-nodes.json” file. Does this file have to be copied only in nodes who will be working as minors?
2. How do I create a non-minor node and add it in the network and also keep in sync with others?(basically is there any point in creating a node who is part of the network but isn’t a minor?)
3. Am I correct in assuming that this process will get tedious every time we add a minor node in the network i.e. we have to update “static-nodes.json” file in each previous nodes? Is there any solution to this?
4. I was reading about my question 3, I came across a concept called “bootnodes”. Can you please explain it to us? is there anyway or advantage of using it here in this step?
Hi Pratik
Thanks for your feedback.
You will find my answers for each of your questions:
1. The process mentioned in the 4th article of the series “pair the miners…”, you have used “static-nodes.json” file. Does this file have to be copied only in nodes who will be working as minors?
The file “static-nodes.json” has to be installed in each node (miners and not miners) that are part of your private testnet. In this way, they will be able to discover themselves as part of the same network.
2. How do I create a non-minor node and add it in the network and also keep in sync with others?(basically is there any point in creating a node who is part of the network but isn’t a minor?)
This point is explained in this tutorial: https://chainskills.com/2017/03/27/synchronize-the-raspberry-pi-with-the-private-blockchain-56/
The RPi is a no-miner node and needs to join the private testnet.
3. Am I correct in assuming that this process will get tedious every time we add a minor node in the network i.e. we have to update “static-nodes.json” file in each previous nodes? Is there any solution to this?
The tutorial focused on a “simple” approach to set-up a private testnet with a set of well know nodes that are parts of your network.
There are different ways to set-up such networks as described here: https://github.com/ethereum/go-ethereum/wiki/Connecting-to-the-network
4. I was reading about my question 3, I came across a concept called “bootnodes”. Can you please explain it to us? is there anyway or advantage of using it here in this step?
“bootnodes” is the same concept as “static-nodes.json” file. The difference is that you identify your nodes in the command line of Geth. This technique is more flexible than adding a static-nodes.json file mainly if the ID of one of your nodes may change.
Does that help?
Cheers
Said
Hi Said,
I am facing pretty much the same challenge as described by Billie above, the difference with mine is that although I have stopped and re-started the miners, my nodes are still not synching. Both static-nodes.json files are saved in ~/ChainSkills/miner1 and miner2 directories respectively, and are of course identical.
The contents of both files is as follows:
[
“enode://3145b4bec1d66c619901a8b74daf625c69cd7f3c172b7408262ae37e50097f93cfe49e4cd89843d78d79638662e153886b8f545b6324554c5e6563b7da2ca4e4@192.168.1.242:30303”,
“enode://0d337f19de7d21a2109793098b72437f40612967531d0816dde7d421c68676f6fd6f06d13692c4fc5dd99881355a2e3f13f0a4497549b338321956ca10755d01@192.168.1.242:30304”
]
Any ideas/suggestions, please?
Many thanks in advance.
Reuben.
Hi, I hope you can answer this. My nodes kind of sync, but: if I use sendTransaction() on node 1, I have to mine on node1 for node2 to see the result; mining on node2 doesn’t do anything to write in another node’s transactions. And vice versa, so it’s not a broken symmetry problem. I don’t see how I can emulate what I eventually want to do: send transactions to the public blockchain, paying to have miners add my data to the chain.