Create a permissioned network
The following steps set up a permissioned network with local node and account permissions. The network uses the Clique Proof of Authority consensus protocol.
Important
A permissioned Ethereum network as described here is not protected against all attack vectors. We recommend applying defense in depth to protect your infrastructure.
Prerequisites
Curl (or similar webservice client)
Steps
Listed on the right-hand side of the page are the steps to create a permissioned network.
1. Create folders
Each node requires a data directory for the blockchain data. When the node starts, Besu saves the node key in this directory.
Create directories for your permissioned network, each of the three nodes, and a data directory for each node:
Permissioned-Network/
├── Node-1
│ ├── data
├── Node-2
│ ├── data
└── Node-3
├── data
2. Get the address of Node-1
In networks using Clique, you must include the address of at least one initial signer in the genesis file. For this network, we’ll use Node-1 as the initial signer. This requires obtaining the address for Node-1.
To retrieve the address for Node-1, in the Node-1
directory, use the public-key export-address
subcommand to write the node address to the specified file (nodeAddress1
in this example).
besu --data-path=data public-key export-address --to=data/nodeAddress1
besu --data-path=data public-key export-address --to=data\nodeAddress1
3. Create the genesis file
The genesis file defines the genesis block of the blockchain (that is, the start of the blockchain). The Clique genesis file includes the address of Node-1 as the initial signer in the extraData
field.
All nodes in a network must use the same genesis file.
Copy the following genesis definition to a file called cliqueGenesis.json
and save it in the Permissioned-Network
directory:
{
"config":{
"chainId":1981,
"constantinoplefixblock": 0,
"clique":{
"blockperiodseconds":15,
"epochlength":30000
}
},
"coinbase":"0x0000000000000000000000000000000000000000",
"difficulty":"0x1",
"extraData":"0x0000000000000000000000000000000000000000000000000000000000000000<Node 1 Address>0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit":"0xa00000",
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce":"0x0",
"timestamp":"0x5c51a607",
"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"
}
},
"number":"0x0",
"gasUsed":"0x0",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"
}
In extraData
, replace <Node 1 Address>
with the address for Node-1, excluding the 0x prefix.
Example
{
...
"extraData":"0x0000000000000000000000000000000000000000000000000000000000000000b9b81ee349c3807e46bc71aa2632203c5b4620340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
...
}
Warning
Do not use the accounts in the genesis file on MainNet or any public network except for testing. The private keys display, which means the accounts are not secure.
4. Create the permissions configuration file
The permissions configuration file defines the nodes and accounts allowlists.
Copy the following permissions configuration to a file called permissions_config.toml
and save a copy in the Node-1/data
, Node-2/data
, and Node-3/data
directories:
permissions_config.toml
accounts-allowlist=["0xfe3b557e8fb62b89f4916b721be55ceb828dbd73", "0x627306090abaB3A6e1400e9345bC60c78a8BEf57"]
nodes-allowlist=[]
The permissions configuration file includes the first two accounts from the genesis file.
Use the JSON-RPC API to add permissioned nodes after starting the nodes.
Note
You specify permissions at the node level. Save the [permissions_config.toml
] file in the data directory for each node.
5. Start Node-1
Use the following command:
besu --data-path=data --genesis-file=../cliqueGenesis.json --permissions-nodes-config-file-enabled --permissions-accounts-config-file-enabled --rpc-http-enabled --rpc-http-api=ADMIN,ETH,NET,PERM,CLIQUE --host-allowlist="*" --rpc-http-cors-origins="*"
besu --data-path=data --genesis-file=..\cliqueGenesis.json --permissions-nodes-config-file-enabled --permissions-accounts-config-file-enabled --rpc-http-enabled --rpc-http-api=ADMIN,ETH,NET,PERM,CLIQUE --host-allowlist="*" --rpc-http-cors-origins="*"
The command line allows you to enable:
- Nodes and accounts permissions using the [
--permissions-nodes-config-file-enabled
] and [--permissions-accounts-config-file-enabled
] options - The JSON-RPC API using the
--rpc-http-enabled
option - The ADMIN, ETH, NET, PERM, and CLIQUE APIs using the
--rpc-http-api
option - All-host access to the HTTP JSON-RPC API using the
--host-allowlist
option - All-domain access to the node through the HTTP JSON-RPC API using the
--rpc-http-cors-origins
option.
When the node starts, the enode URL displays. You need the enode URL to specify Node-1 as a peer and update the permissions configuration file in the following steps.
6. Start Node-2
Start another terminal, change to the Node-2
directory, and start Node-2:
besu --data-path=data --genesis-file=../cliqueGenesis.json --permissions-nodes-config-file-enabled --permissions-accounts-config-file-enabled --rpc-http-enabled --rpc-http-api=ADMIN,ETH,NET,PERM,CLIQUE --host-allowlist="*" --rpc-http-cors-origins="*" --p2p-port=30304 --rpc-http-port=8546
besu --data-path=data --genesis-file=..\cliqueGenesis.json --permissions-nodes-config-file-enabled --permissions-accounts-config-file-enabled --rpc-http-enabled --rpc-http-api=ADMIN,ETH,NET,PERM,CLIQUE --host-allowlist="*" --rpc-http-cors-origins="*" --p2p-port=30304 --rpc-http-port=8546
The command line specifies:
- A different port to Node-1 for P2P discovery using the
--p2p-port
option - A different port to Node-1 for HTTP JSON-RPC using the
--rpc-http-port
option - A data directory for Node-2 using the
--data-path
option - Other options as for as for Node-1.
When the node starts, the enode URL is displays. You need the enode URL to update the permissions configuration file in the following steps.
7. Start Node-3
Start another terminal, change to the Node-3
directory, and start Node-3:
besu --data-path=data --genesis-file=../cliqueGenesis.json --permissions-nodes-config-file-enabled --permissions-accounts-config-file-enabled --rpc-http-enabled --rpc-http-api=ADMIN,ETH,NET,PERM,CLIQUE --host-allowlist="*" --rpc-http-cors-origins="*" --p2p-port=30305 --rpc-http-port=8547
besu --data-path=data --genesis-file=..\cliqueGenesis.json --permissions-nodes-config-file-enabled --permissions-accounts-config-file-enabled --rpc-http-enabled --rpc-http-api=ADMIN,ETH,NET,PERM,CLIQUE --host-allowlist="*" --rpc-http-cors-origins="*" --p2p-port=30305 --rpc-http-port=8547
The command line specifies:
- A different port to Node-1 and Node-2 for P2P 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 - A data directory for Node-3 using the
--data-path
option - Other options as for as for Node-1.
When the node starts, the enode URL is displays. You need the enode URL to update the permissions configuration file in the following steps.
8. Add enode URLs for nodes to permissions configuration file
Start another terminal and use the perm_addNodesToAllowlist
JSON-RPC API method to add the nodes to the permissions configuration file for each node.
Replace <EnodeNode1>
, <EnodeNode2>
, and <EnodeNode3>
with the enode URL displayed when starting each node.
Node-1:
curl -X POST --data '{"jsonrpc":"2.0","method":"perm_addNodesToAllowlist","params":[["<EnodeNode1>","<EnodeNode2>","<EnodeNode3>"]], "id":1}' http://127.0.0.1:8545
Node-2:
curl -X POST --data '{"jsonrpc":"2.0","method":"perm_addNodesToAllowlist","params":[["<EnodeNode1>","<EnodeNode2>","<EnodeNode3>"]], "id":1}' http://127.0.0.1:8546
Node 3:
curl -X POST --data '{"jsonrpc":"2.0","method":"perm_addNodesToAllowlist","params":[["<EnodeNode1>","<EnodeNode2>","<EnodeNode3>"]], "id":1}' http://127.0.0.1:8547
Tip
The cURL call is the same for each node except for the JSON-RPC endpoint.
9. Add nodes as peers
Use the admin_addPeer
JSON-RPC API method to add Node-1 as a peer for Node-2 and Node-3.
Replace <EnodeNode1>
with the enode URL displayed when starting Node-1.
Node 2:
curl -X POST --data '{"jsonrpc":"2.0","method":"admin_addPeer","params":["<EnodeNode1>"],"id":1}' http://127.0.0.1:8546
Node 3:
curl -X POST --data '{"jsonrpc":"2.0","method":"admin_addPeer","params":["<EnodeNode1>"],"id":1}' http://127.0.0.1:8547
Tip
The cURL call is the same for both nodes except for the JSON-RPC endpoint.
Replace <EnodeNode2>
with the enode URL displayed when starting Node-2.
Node 3:
curl -X POST --data '{"jsonrpc":"2.0","method":"admin_addPeer","params":["<EnodeNode2>"],"id":1}' http://127.0.0.1:8547
10. Confirm permissioned network is working
Check peer count
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 (the node running the JSON-RPC service) has two peers (Node-2 and Node-3):
{
"jsonrpc" : "2.0",
"id" : 1,
"result" : "0x2"
}
Send a transaction from an account in the allowlist
Import the first account from the genesis file into MetaMask and send transactions, as described in
Account 1
- Address:
0xfe3b557e8fb62b89f4916b721be55ceb828dbd73
- Private key :
0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63
- Initial balance :
0xad78ebc5ac6200000
(200000000000000000000 in decimal)
Info
Besu does not support private key management.
Try sending a transaction from an account not in the accounts allowlist
Import the last account from the genesis file into MetaMask and try to send a transactions, as described in Private Network Example Tutorial:
Account 3
- Address:
0xf17f52151EbEF6C7334FAD080c5704D77216b732
- Private key :
0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f
- Initial balance :
0x90000000000000000000000
(2785365088392105618523029504 in decimal)
Start a node not on the nodes allowlist
In your Permissioned-Network
directory, create a Node-4
directory and data
directory inside it.
Change to the Node-4
directory and start Node-4 specifying the Node-1 enode URL as the bootnode:
besu --data-path=data --bootnodes="<EnodeNode1>" --genesis-file=../cliqueGenesis.json --rpc-http-enabled --rpc-http-api=ADMIN,ETH,NET,PERM,CLIQUE --host-allowlist="*" --rpc-http-cors-origins="*" --p2p-port=30306 --rpc-http-port=8548
besu --data-path=data --bootnodes="<EnodeNode1>" --genesis-file=..\cliqueGenesis.json --rpc-http-enabled --rpc-http-api=ADMIN,ETH,NET,PERM,CLIQUE --host-allowlist="*" --rpc-http-cors-origins="*" --p2p-port=30306 --rpc-http-port=8548
Start another terminal and use cURL to call the JSON-RPC API net_peerCount
method:
curl -X POST --data '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}' localhost:8548
The result confirms Node-4 has no peers even though it specifies Node-1 as a bootnode:
{
"jsonrpc" : "2.0",
"id" : 1,
"result" : "0x0"
}
Stop nodes
When finished using the permissioned network, stop all nodes using Ctrl+C in each terminal window.
Tip
To restart the permissioned network in the future, start from 5. Start Node-1.