© Wei-Meng Lee 2019
W.-M. LeeBeginning Ethereum Smart Contracts Programminghttps://doi.org/10.1007/978-1-4842-5086-0_4

4. Creating Your Own Private Ethereum Test Network

Wei-Meng Lee1 
(1)
Ang Mo Kio, Singapore
 

In the previous chapter, you saw how to download and install the Geth client. You also saw how to connect to the various Ethereum networks and how to examine the blockchain using the blockchain explorer – EtherScan.

One useful feature of Geth is that you can use it to create your own private test network in your local setup, without connecting to the real blockchain. This makes the development work much easier and allow you to explore the Ethereum blockchain without needing to pay for real Ether. Hence, in this chapter you will see how you can create your own private Ethereum test network, as well as how to connect to peers and perform transactions such as sending Ethers between accounts.

Creating the Private Ethereum Test Network

For this example, we are going to create a private test network comprising of two nodes in a single computer – node1 and node2, and a third node – node3, on another computer (see Figure 4-1).
../images/471881_1_En_4_Chapter/471881_1_En_4_Fig1_HTML.jpg
Figure 4-1

Our private Ethereum test network

These three nodes will form your own test network where you can do things like mining, transferring Ethers to another account, and in the next chapter, deploy your Smart Contracts.

Creating the Genesis Block

Before you proceed to create your own private Ethereum test network, you need to create the genesis block . The genesis block is the start of the blockchain –  the first block (block 0), and the only block that does not point to a predecessor block. The Ethereum protocol ensures that no other node will agree with your version of the blockchain unless they have the same genesis block, so you can make as many private testnet blockchains as you’d like.

For this chapter, you will create a folder named MyTestNet on your computer. For simplicity, I will create this folder in my home directory:
$ cd ~
$ mkdir MyTestNet
$ cd MyTestNet

For Windows users, the home directory is typically C:users<user_name>.

To create the genesis block, create a file named genesisblock.json and populate it as follows:
{
  "config": {
        "chainId": 10,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
  "alloc"      : {},
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00"
}

Creating a Folder for Storing Node Data

Next, you need to create a directory to store the data for all the nodes in your private test network. For this, we shall create a directory named data in the MyTestNet folder:
$ cd ~/MyTestNet
$ mkdir data
Figure 4-2 shows the directory structure at this point.
../images/471881_1_En_4_Chapter/471881_1_En_4_Fig2_HTML.png
Figure 4-2

The content of the MyTestNet directory

Initiating a Blockchain Node

To create a node on the test network, you need to initialize it using the genesis block you have created earlier. You can do it using the following command:
$ geth --datadir ~/MyTestNet/data/node1 init
~/MyTestNet/genesisblock.json
The preceding creates a node named node1 and saves all its data in the node1 directory. You should see the following response:
INFO [01-08|13:11:00.937] Maximum peer count                       ETH=25 LES=0 total=25
INFO [01-08|13:11:00.947] Allocated cache and file handles         database=/Users/weimenglee/MyTestNet/data/node1/geth/chaindata cache=16 handles=16
INFO [01-08|13:11:00.950] Writing custom genesis block
INFO [01-08|13:11:00.950] Persisted trie from memory database      nodes=0 size=0.00B time=11.47μs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [01-08|13:11:00.950] Successfully wrote genesis state         database=chaindata                                             hash=5e1fc7...d790e0
INFO [01-08|13:11:00.950] Allocated cache and file handles         database=/Users/weimenglee/MyTestNet/data/node1/geth/lightchaindata cache=16 handles=16
INFO [01-08|13:11:00.952] Writing custom genesis block
INFO [01-08|13:11:00.952] Persisted trie from memory database      nodes=0 size=0.00B time=1.856μs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [01-08|13:11:00.952] Successfully wrote genesis state         database=lightchaindata                                             hash=5e1fc7...d790e0
Figure 4-3 shows the content of node1 after this command is run.
../images/471881_1_En_4_Chapter/471881_1_En_4_Fig3_HTML.png
Figure 4-3

The content of the node1 directory

The Geth directory contains two folders for storing the blockchains – chaindata and lightchaindata, while the keystore directory contains accounts information (more on this later).

Let’s create another node, this time let’s call it node2:
$ geth --datadir ~/MyTestNet/data/node2 init
~/MyTestNet/genesisblock.json
Figure 4-4 shows the current state of the MyTestNet directory.
../images/471881_1_En_4_Chapter/471881_1_En_4_Fig4_HTML.png
Figure 4-4

The node2 directory is now added to the data directory

Starting Up the Nodes

Now that the nodes have been initialized, let’s start them up. Let’s start up node1:
$ geth --datadir ~/MyTestNet/data/node1 console 2>console1.log
Once node1 is started up, you will see the Geth JavaScript Console:
Welcome to the Geth JavaScript console!
instance: Geth/v1.8.20-stable/darwin-amd64/go1.11.4
 modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
>

Tip

The Geth JavaScript Console provides an interactive console for you to interact with the Ethereum blockchain using the JavaScript language.

The console 2 option basically redirects the output to a file (named console1.log in this example). Without this redirection, Geth would continually generate a lot of output and the Geth JavaScript Console would not be usable.

Creating Accounts

Now that node1 is started up, you can create a new account in your node using the personal.newAccount() function:
> personal.newAccount()
Passphrase: <password>
Repeat passphrase: <password>
"0xe225dc6b5e85b6f2659ca310b2a17247291e6b6b"

The web3 object (from the web3.js library) allows you to programmatically interact with the Ethereum blockchain. It also exposes the eth object, which itself also exposes the personal object. Hence the full name for the personal.newAccount() function is actually web3.eth.personal.newAccount(). The personal object allows you to interact with the Ethereum node’s accounts.

You can be asked to enter a password for the new account. Once that is done, the public address of the account is displayed.

To show the list of accounts in your node, use the eth.accounts property:
> eth.accounts
["0xe225dc6b5e85b6f2659ca310b2a17247291e6b6b"]

The list of accounts will be shown as an array. In the example here, there is only one account.

Once the account is created, you will be able to find the account details stored in a file (named beginning with the UTC word) in the ~/MyTestNet/data/node1/keystore directory. We will talk more about this later in this chapter.

Checking the Balance of an Account

To check the balance of an account, use the eth.getBalance() function :
> eth.getBalance(eth.accounts[0])
0

As mentioned earlier, the eth object is derived from the web3 object, so the preceding function is equivalent to web3.eth.getBalance(web3.eth.accounts[0]).

Apparently, at this moment has no ethers, so you will see a 0. However, do note that the unit displayed for the balance is Wei, where 1 Ether is 1000000000000000000 Wei (1 followed by 18 zeros). Very often, you do not want to see the units displayed in Wei but rather in Ether. To make your life easier, you can use the web3.fromWei() function , like the following command, to convert the balance in Wei to Ether:
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
0
Table 4-1 shows the different units in Ethereum.
Table 4-1

Units in Ethereum

Unit

Wei Value

Wei

wei

1 wei

1

Kwei (babbage)

103 wei

1,000

Mwei (lovelace)

106 wei

1,000,000

Gwei (shannon)

109 wei

1,000,000,000

microether (szabo)

1012 wei

1,000,000,000,000

milliether (finney)

1015 wei

1,000,000,000,000,000

ether

1018 wei

1,000,000,000,000,000,000

Stopping the Node

To stop the node, simply use the exit command:
> exit

For now, let’s stop node1.

Starting Another Node

Now that we have started node1 and then stopped it, let’s now start node2. In a new Terminal window, type the following command:
$ geth --datadir ~/MyTestNet/data/node2 --port 30304
--nodiscover --networkid 2345 console 2>console2.log

Observe that in the preceding command, I have specified the --port option and set the port to 30304. This is because Geth by default uses port 30303, and if you have multiple nodes running on the same computer, each node must use a unique port number. By setting this to port 30304, it will prevent conflicting with another node using the default port. The --nodiscover option means that peers will not automatically discover each other and that they need to be added manually. The --networkid option specifies the network id so that other nodes can attach to the network with the same network id.

Once node2 is started, restart node1 with the following command:
$ geth --datadir ~/MyTestNet/data/node1 --networkid 2345 console 2>console1.log
Notice that node1 is started with the --networkid option with the value of 2345. This is required so that it can be added as a peer to node2 later on. Figure 4-5 shows the state of the two nodes at this moment.
../images/471881_1_En_4_Chapter/471881_1_En_4_Fig5_HTML.jpg
Figure 4-5

The two nodes currently running within the same computer

Getting Information About the Node

Now that the two nodes are up and running, let’s get some detailed information about each of them. Type the following command in node1:
> admin.nodeInfo
{
  enode: "enode://177be9b5b6beb0fb045bb35e5dea443016187fb0d7fdddbcce4b520d554ec4345a4f07702bca8ca0204107612ba39ae5115257350b5b31174239ad59168312aa@138.75.206.153:30303",
  enr: "0xf896b84076d1c710e54b570ae6f96ed94cadce07025e89d0b178ef82d97f78ed00cab1bf0e0c752c86eb7cafce38cc538114eb3284a37e7b1267283c4d9bd292b49e289f0383636170c6c5836574683f826964827634826970848a4bce9989736563703235366b31a102177be9b5b6beb0fb045bb35e5dea443016187fb0d7fdddbcce4b520d554ec4348374637082765f8375647082765f",
  id: "142e0fc58a6598b51d9f5ecdee56e60836a9cd58e7717234df3e6514f84ffc03",
  ip: "138.75.206.153",
  listenAddr: "[::]:30303",
  name: "Geth/v1.8.20-stable/darwin-amd64/go1.11.4",
  ports: {
    discovery: 30303,
    listener: 30303
  },
  protocols: {
    eth: {
      config: {
        chainId: 10,
        eip150Hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
        eip155Block: 0,
        eip158Block: 0,
        homesteadBlock: 0
      },
      difficulty: 131072,
      genesis: "0x5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0",
      head: "0x5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0",
      network: 2345
    }
  }
}

The admin object is derived from the web3 object. Hence the full name of admin.addPeer() is web3.admin.addPeer(). The admin object allows you to interact with the underlying blockchain.

You will a whole bunch of information. In particular, take note of the preceding enode key (bolded for emphasis). At the end of the enode value, observe the port number 30303 (which is the port node 1 is using).

Tip

An enode describes a node in the Ethereum network in the form of an URI.

Pairing the Nodes

Copy the value of the enode key in node1, and in node2, type the following command:
> admin.addPeer("enode://177be9b5b6beb0fb045bb35e5dea443016187fb0d7fdddbcce4b520d554ec4345a4f07702bca8ca0204107612ba39ae5115257350b5b31174239ad59168312aa@138.75.206.153:30303")

Caution

In the preceding example, the 138.75.206.153 refers to my computer’s public IP address. When pairing with another node on the same computer/network, it is important to replace this IP address with that of the local IP address of the computer. If you don’t do this, the two nodes will not be paired correctly.

In the preceding command, the bolded portion is the value of the enode key of node1. The admin.addPeer() function adds a peer to the current node using the peer’s enode value.

To verify that the peer is added successfully, use the admin.peers property :
> admin.peers
[{
    caps: ["eth/63"],
    enode: "enode://177be9b5b6beb0fb045bb35e5dea443016187fb0d7fdddbcce4b520d554ec4345a4f07702bca8ca0204107612ba39ae5115257350b5b31174239ad59168312aa@138.75.206.153:30303",
    id: "142e0fc58a6598b51d9f5ecdee56e60836a9cd58e7717234df3e6514f84ffc03",
    name: "Geth/v1.8.20-stable/darwin-amd64/go1.11.4",
    network: {
      inbound: false,
      localAddress: "192.168.1.116:63908",
      remoteAddress: "138.75.206.153:30303",
      static: true,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 131072,
        head: "0x5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0",
        version: 63
      }
    }
}]
If the peer is added successfully, you should see the preceding output. From the output, you can see that the node1’s IP address is 192.168.1.116, which is the IP address of my computer. Figure 4-6 shows the current state of our private test network.
../images/471881_1_En_4_Chapter/471881_1_En_4_Fig6_HTML.jpg
Figure 4-6

The current state of our private test network, with two peered nodes

The admin.peers property returns a [] if there is currently no peer attached to the node.

So far, we have been pairing the nodes within the same computer. How do you pair nodes from another computer?

Note

I will leave the creation of the third node as an exercise for the reader.

Suppose you have another node – node3, running on another computer. On the Geth JavaScript Console on that node, you can add node1 as a peer by using the following command:
> admin.addPeer("enode://177be9b5b6beb0fb045bb35e5dea443016
187fb0d7fdddbcce4b520d554ec4345a4f07702bca8ca0204107612ba39
ae5115257350b5b31174239ad59168312aa@192.168.1.116:30303")
You just need to replace the IP address and port number of the node with that of the node you are trying to add to. In this example, node1 is running on port 30303, and its IP address is 192.168.1.116. Figure 4-7 summarizes the state of the nodes at this moment.
../images/471881_1_En_4_Chapter/471881_1_En_4_Fig7_HTML.jpg
Figure 4-7

Our private test network with three connected nodes

Performing Mining

With all the nodes connected, we can start to perform some mining operations! Before we do that, let’s verify the block numbers for the current blockchain. In any of the nodes, you can use the eth.blockNumber property to display the latest block number in the blockchain:
> eth.blockNumber
0
As expected, we should see 0. This is because at this moment the blockchain has only one block – the genesis block. To start the mining on node1, use the miner.start() function :
> miner.start(1)
null

The number you passed into the start() function is the number of threads you want to use for the mining operation. Don’t be alarmed with the null result. The null simply means that the function has nothing to return to you; it does not indicate the failure of the mining operation.

Note

The miner object is derived from the web3 object.

The node1 will now start the mining operation. On some computers, it will take a few minutes to mine the first block, while on some slower machines, it will take a much longer time. So be patient.

You can verify that a block has been mined by checking the result of the eth.blockNumber property. If the block number is more than 0, then you have mined your first block! Congratulations!

And since node2 is connected to node1, you can also verify the block number in node2. You should see the same block number.

Caution

If node2 is not seeing the same block number as node1, it means that the two are not paired up correctly.

If you need to stop the mining, you can use the miner.stop() function. For now, leave the mining on.

Tip

This is a good time to check the balance of your account. If you have managed to mine a block, you should have some Ethers in your account now.

Examining a Block

You can examine the content of a block by using the eth.getBlock() function:
> eth.getBlock(22)
{
  difficulty: 132352,
  extraData: "0xd983010814846765746888676f312e31312e348664617277696e",
  gasLimit: 3209750,
  gasUsed: 0,
  hash: "0xc939ead8408e911bd7fa8e4dde37b08e987c624c48f5df2379928b32de4f2021",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0xe225dc6b5e85b6f2659ca310b2a17247291e6b6b",
  mixHash: "0x18ce27b4d533e1611971c20518e654d00c8d9fd86af4e50fc50537eb8fb2e1f8",
  nonce: "0x430debc6fcf6ec20",
  number: 22,
  parentHash: "0xf74385c3e2d88b43da869b5389745b45163baa3a314961f7311a8ba8abb057e6",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 537,
  stateRoot: "0x9356e29a82c2b597fff48b36e1b1b39a9e57253d5c2934239d92c406b63589c1",
  timestamp: 1546932855,
  totalDifficulty: 3028096,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}

The eth.getBlock() function takes in a number representing the block number that you want to examine. One particular interesting point to note: the miner key indicates the account that successfully mined the block.

Mining on Both Nodes

Up till this point, only node1 is doing the mining and having all the fun (and reaping all the rewards). Why not get node2 to do the mining too? If you try to mine on node2 now, you will see the following error:
> miner.start(1)
Error: etherbase missing: etherbase must be explicitly specified
    at web3.js:3143:20
    at web3.js:6347:15
    at web3.js:5081:36
    at <anonymous>:1:1

Why? Simple, in order to perform mining, you need to have at least one account in your node for the rewards to be deposited into. To solve this problem, create a new account using the personal.newAccount() function. Once the account is created, you can use the miner.start() function again.

You now have two miners mining at the same time and competing for rewards. To know who is the miner of the latest block, you can use the eth.getBlock() function and checking its miner property, like this: eth.getBlock(eth.blockNumber).miner . The result would be the address of the account who managed to mine the latest block.

Transferring Ethers Between Nodes

In node1, let’s create another account using the personal.newAccount() function. You should now have two accounts:
> eth.accounts
["0x530e822163471b0e65725cbd85dc141ff6b24d59", "0xf439200bdfb03598e9887828399e8986447d658f"]
Let's verify how much you have in each account:
> eth.getBalance(eth.accounts[0])
3.376328125e+22
> eth.getBalance(eth.accounts[1])
0
To transfer some Ethers from one account to another account, you can use the eth.sendTransaction() function. But before you use it to transfer Ethers, you need to unlock the source account first, using the personal.unlockAccount() function:
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x530e822163471b0e65725cbd85dc141ff6b24d59
Passphrase: <password>
true
Once the account is unlocked, you can now transfer the Ether:
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(5,"ether")})
"0xbac74dffae71c5532d83ed8ae37ff97d68dd2ab8b7f62fa2b1032f88df8d543c"

Tip

If you want to transfer Ether to another node on another computer, simply specify the address of the account you want to send to enclosed with a pair of double quotes, like this: eth.sendTransaction({from: eth.accounts[0], to: "0x9ba6f3c9cce2b172d0a85a50101ae05f3b4c8731", value: web3.toWei(5,"ether")})

In the preceding example, I am transferring five Ethers from the first account to the second account within the same node. The output of the function is the transaction ID. If you now check the balance of the two accounts and realize that they are still the same, then one of the following causes is likely:
  • You are not currently mining. Remember, mining confirms transactions so that the transactions can be recorded on the blockchain. To resolve this, start mining on the node.

  • If you are currently mining, then it is likely that the transactions have not be confirmed yet. Will a while and try to check the balance again.

After a while, you should be able to see five Ethers in the second account:
> eth.getBalance(eth.accounts[1])
5000000000000000000

Tip

If you check the balance of the first account, you are likely to see it has less than five Ethers deducted. This is because in spite of the five Ethers deducted, it is also earning rewards doing the mining. Hence, it is easier to verify the balance of the second account.

Managing Accounts

Earlier in this chapter, you learned about creating accounts in your node. I also mentioned that the account details are stored in a file with the name starting with “UTC” and saved in the ~/MyTestNet/data/node1/keystore directory. Let’s try to dissect the content of this UTC file:
{
  "address": "530e822163471b0e65725cbd85dc141ff6b24d59",
  "crypto": {
    "cipher": "aes-128-ctr",
    "ciphertext": "4f817ca1925f3c54a3874e9075eb74324d6976cdc7fe1d44372457b832a23987",
    "cipherparams": {
      "iv": "043fab979c36a35b53a84611e771670c"
    },
    "kdf": "scrypt",
    "kdfparams": {
      "dklen": 32,
      "n": 262144,
      "p": 1,
      "r": 8,
      "salt": "c5743e378efe354744c35e3b6cad2ae2eb1407890731fae7fc9acb085931c343"
    },
    "mac": "1f8e42a800824ddab06fb92c5ca8ad874f1830c39d575d4f16c4118943de8803"
  },
  "id": "00d6ad8d-96c1-4211-84ac-1d921617bd8e",
  "version": 3
}

The preceding shows the content of the first account in node1 with the file name UTC--2019-01-08T08-37-52.567662000Z--530e822163471b0e65725cbd85dc141ff6b24d59.

It contains the following:
  • Your encrypted private key (encrypted using your supplied password).

  • The public key is not stored in the JSON file as it can be derived from the private key.

  • Your account address (which is derived from your public key). The account address is the last 20 bytes of the public key.

Figure 4-8 summarizes how your account address is derived.
../images/471881_1_En_4_Chapter/471881_1_En_4_Fig8_HTML.jpg
Figure 4-8

Understanding how the accounts information is derived

Tip

Curious about your private key? You can go to www.myetherwallet.com/#view-wallet-info , select the Keystore File (UTC / JSON) option, and upload your JSON UTC file. You will be asked to enter your password used to secure the account, and voila, your private key will now be displayed. Note that this is purely for educational purposes. Do not try this with your real account.

Removing Accounts

Once you use the personal.newAccount() function, the account is created. There is no equivalent function to remove the account. The easiest way to delete the account is to go to the ~/MyTestNet/data/node1/keystore directory and delete the UTC file corresponding to the account that you want to delete.

Setting the Coinbase

The eth.coinbase property returns the account within the node that all the mining rewards go to. In node1, our coinbase is our first account:
> eth.coinbase
"0x530e822163471b0e65725cbd85dc141ff6b24d59"

To change the coinbase, you can use the miner.setEtherbase() function. Let’s try it now.

First, let’s print out the accounts that we have in node1:
> eth.accounts
["0x530e822163471b0e65725cbd85dc141ff6b24d59", "0xf439200bdfb03598e9887828399e8986447d658f"]
Let's change the coinbase to the second account:
> miner.setEtherbase(eth.accounts[1])
true
Once this is done, we can verify if the coinbase has indeed been changed to the second account:
> eth.coinbase
"0xf439200bdfb03598e9887828399e8986447d658f"

Summary

In this chapter, you have learned how to use Geth to create your own private Ethereum test network. You have learn how to create accounts in your node, connect to other nodes, transfer Ether between nodes, and more. Deploying your own test network is much more efficient than using one of Ethereum’s test network. What’s more, it allows you to experiment and have a deeper understanding of Ethereum.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.89.238