Updated on July 29, 2020

Create a private network using the IBFT 2.0 (Proof of Authority) consensus protocol

A private network provides a configurable network for testing. This private network uses the [IBFT 2.0 (Proof of Authority) consensus protocol].

Important

The steps in this tutorial create an isolated, but not protected or secure, Ethereum private network. We recommend running the private network behind a properly configured firewall.

This tutorial configures a private network using IBFT 2.0 for educational purposes only. IBFT 2.0 requires 4 validators to be Byzantine fault tolerant.

Prerequisites

Steps

Listed on the right-hand side of the page are the steps to create a private network using IBFT 2.0 with four nodes. The four nodes are all validators.

1. Create directories

Each node requires a data directory for the blockchain data.

Create directories for your private network, each of the four nodes, and a data directory for each node:

IBFT-Network/
├── Node-1
│   ├── data
├── Node-2
│   ├── data
├── Node-3
│   ├── data
└── Node-4
    ├── data

2. Create a configuration file

The configuration file defines the IBFT 2.0 genesis file and the number of node key pairs to generate.

The configuration file has two subnested JSON nodes. The first is the genesis property defining the IBFT 2.0 genesis file, except for the extraData string, which Besu generates automatically in the resulting genesis file. The second is the blockchain property defining the number of key pairs to generate.

Copy the following configuration file definition to a file called ibftConfigFile.json and save it in the IBFT-Network directory:

{
 "genesis": {
   "config": {
      "chainId": 2018,
      "muirglacierblock": 0,
      "ibft2": {
        "blockperiodseconds": 2,
        "epochlength": 30000,
        "requesttimeoutseconds": 4
      }
    },
    "nonce": "0x0",
    "timestamp": "0x58ee40ba",
    "gasLimit": "0x47b760",
    "difficulty": "0x1",
    "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
    "coinbase": "0x0000000000000000000000000000000000000000",
    "alloc": {
       "fe3b557e8fb62b89f4916b721be55ceb828dbd73": {
          "privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
          "comment": "private key and this comment are ignored.  In a real chain, the private key should NOT be stored",
          "balance": "0xad78ebc5ac6200000"
       },
       "627306090abaB3A6e1400e9345bC60c78a8BEf57": {
         "privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
         "comment": "private key and this comment are ignored.  In a real chain, the private key should NOT be stored",
         "balance": "90000000000000000000000"
       },
       "f17f52151EbEF6C7334FAD080c5704D77216b732": {
         "privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
         "comment": "private key and this comment are ignored.  In a real chain, the private key should NOT be stored",
         "balance": "90000000000000000000000"
       }
      }
 },
 "blockchain": {
   "nodes": {
     "generate": true,
       "count": 4
   }
 }
}

Warning

Do not use the accounts in alloc in the genesis file on MainNet or any public network except for testing. The private keys display, which means the accounts are not secure.

3. Generate node keys and a genesis file

In the IBFT-Network directory, generate the node key and genesis file:

besu operator generate-blockchain-config --config-file=ibftConfigFile.json --to=networkFiles --private-key-file-name=key

Besu creates the following in the networkFiles directory:

  • genesis.json - The genesis file including the extraData property specifying the four nodes are validators.
  • A directory for each node named using the node address and containing the public and private key for each node.
networkFiles/
├── genesis.json
└── keys
    ├── 0x438821c42b812fecdcea7fe8235806a412712fc0
    │   ├── key
    │   └── key.pub
    ├── 0xca9c2dfa62f4589827c0dd7dcf48259aa29f22f5
    │   ├── key
    │   └── key.pub
    ├── 0xcd5629bd37155608a0c9b28c4fd19310d53b3184
    │   ├── key
    │   └── key.pub
    └── 0xe96825c5ab8d145b9eeca1aba7ea3695e034911a
        ├── key
        └── key.pub

4. Copy the genesis file to the IBFT-Network directory

Copy the genesis.json file to the IBFT-Network directory.

5. Copy the node private keys to the node directories

For each node, copy the key files to the data directory for that node

IBFT-Network/
├── genesis.json
├── Node-1
│   ├── data
│   │    ├── key
│   │    ├── key.pub
├── Node-2
│   ├── data
│   │    ├── key
│   │    ├── key.pub
├── Node-3
│   ├── data
│   │    ├── key
│   │    ├── key.pub
├── Node-4
│   ├── data
│   │    ├── key
│   │    ├── key.pub

6. Start the first node as the bootnode

In the Node-1 directory, start Node-1:

besu --data-path=data --genesis-file=../genesis.json --rpc-http-enabled --rpc-http-api=ETH,NET,IBFT --host-allowlist="*" --rpc-http-cors-origins="all"
besu --data-path=data --genesis-file=..\genesis.json --rpc-http-enabled --rpc-http-api=ETH,NET,IBFT --host-allowlist="*" --rpc-http-cors-origins="all"

The command line:

When the node starts, the enode URL displays. Copy the enode URL to specify Node-1 as the bootnode in the following steps.

Node 1 Enode URL

7. Start Node-2

Start another terminal, change to the Node-2 directory and start Node-2 specifying the Node-1 enode URL copied when starting Node-1 as the bootnode:

besu --data-path=data --genesis-file=../genesis.json --bootnodes=<Node-1 Enode URL> --p2p-port=30304 --rpc-http-enabled --rpc-http-api=ETH,NET,IBFT --host-allowlist="*" --rpc-http-cors-origins="all" --rpc-http-port=8546
besu --data-path=data --genesis-file=..\genesis.json --bootnodes=<Node-1 Enode URL> --p2p-port=30304 --rpc-http-enabled --rpc-http-api=ETH,NET,IBFT --host-allowlist="*" --rpc-http-cors-origins="all" --rpc-http-port=8546

The command line specifies:

  • The data directory for Node-2 using the --data-path option.
  • A different port to Node-1 for P2P peer discovery using the --p2p-port option.
  • A different port to Node-1 for HTTP JSON-RPC using the --rpc-http-port option.
  • The enode URL for Node-1 using the --bootnodes option.
  • Other options as for Node-1.

8. Start Node-3

Start another terminal, change to the Node-3 directory and start Node-3 specifying the Node-1 enode URL copied when starting Node-1 as the bootnode:

besu --data-path=data --genesis-file=../genesis.json --bootnodes=<Node-1 Enode URL> --p2p-port=30305 --rpc-http-enabled --rpc-http-api=ETH,NET,IBFT --host-allowlist="*" --rpc-http-cors-origins="all" --rpc-http-port=8547
besu --data-path=data --genesis-file=..\genesis.json --bootnodes=<Node-1 Enode URL> --p2p-port=30305 --rpc-http-enabled --rpc-http-api=ETH,NET,IBFT --host-allowlist="*" --rpc-http-cors-origins="all" --rpc-http-port=8547

The command line specifies:

  • The data directory for Node-3 using the --data-path option.
  • A different port to Node-1 and Node-2 for P2P peer discovery using the --p2p-port option.
  • A different port to Node-1 and Node-2 for HTTP JSON-RPC using the --rpc-http-port option.
  • The bootnode as for Node-2.
  • Other options as for Node-1.

9. Start Node-4

Start another terminal, change to the Node-4 directory and start Node-4 specifying the Node-1 enode URL copied when starting Node-1 as the bootnode:

besu --data-path=data --genesis-file=../genesis.json --bootnodes=<Node-1 Enode URL> --p2p-port=30306 --rpc-http-enabled --rpc-http-api=ETH,NET,IBFT --host-allowlist="*" --rpc-http-cors-origins="all" --rpc-http-port=8548
besu --data-path=data --genesis-file=..\genesis.json --bootnodes=<Node-1 Enode URL> --p2p-port=30306 --rpc-http-enabled --rpc-http-api=ETH,NET,IBFT --host-allowlist="*" --rpc-http-cors-origins="all" --rpc-http-port=8548

The command line specifies:

  • The data directory for Node-4 using the --data-path option.
  • A different port to Node-1, Node-2, and Node-3 for P2P peer discovery using the --p2p-port option.
  • A different port to Node-1, Node-2, and Node-3 for HTTP JSON-RPC using the --rpc-http-port option.
  • The bootnode as for Node-2.
  • Other options as for Node-1.

10. Confirm the private network is working

Start another terminal, use curl to call the JSON-RPC API net_peerCount method and confirm the nodes are functioning as peers:

curl -X POST --data '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}' localhost:8545

The result confirms Node-1 has three peers (Node-2, Node-3, and Node-4):

{
  "jsonrpc" : "2.0",
  "id" : 1,
  "result" : "0x3"
}

Next steps

Look at the logs displayed to confirm Besu is producing blocks.

Use the IBFT API to remove or add validators.

Note

To add or remove nodes as validators you need the node address. The directory created for each node has the node address as the name.

This tutorial configures a private network using IBFT 2.0 for educational purposes only. IBFT 2.0 requires four validators to be Byzantine fault tolerant.

Import accounts to MetaMask and send transactions as described in the private network example tutorial.

Info

Besu does not support private key management.

Stop the nodes

When finished using the private network, stop all nodes using Ctrl+C in each terminal window.

Tip

To restart the IBFT 2.0 network in the future, start from 6. Start First Node as Bootnode.