Assigning the Etherium enode url

Posted on

Disclaimer: I’ve tested this implementation on Parity v1.10.4. I’m not sure if it works on older Parity versions or with geth.

Eherium nodes are identified by the enode url. The enode url is composed of a seeminly random hash and the ip and port of the machine it’s deployed on. The enode is shown when starting a node.

Loading config file from /opt/parity/config.toml
2018-06-25 02:29:18 UTC Starting Parity/v1.10.4-stable-39b9f1e-20180514/x86_64-linux-gnu/rustc1.26.0
2018-06-25 02:29:18 UTC Keys path /opt/parity/keys/mynetwork
2018-06-25 02:29:18 UTC DB path /opt/parity/chains/mynetwork/db/a5065b7968d24ce1
2018-06-25 02:29:18 UTC Path to dapps /opt/parity/dapps
2018-06-25 02:29:18 UTC State DB configuration: fast
2018-06-25 02:29:18 UTC Operating mode: active
2018-06-25 02:29:18 UTC Configured for mynetwork using AuthorityRound engine
2018-06-25 02:29:19 UTC Public node URL: enode://661c467b4b643d332e9e12c76aab15f40790c14b12fae8f42e63fbd4fc667ecd7897aca2b6a88e47a038017cb2e67737141d7537b06562e7837d37880f1b3a2da7@173.64.45.32:30303

This makes it difficult to pre-determine the enode of the nodes in your network. For example, if you want to deploy a private network with a few authority nodes you’ll need to specify these nodes in your chain specification file so that each node can find the other nodes when starting.

We have to start the boot nodes one at a time, retrieve the enode for each new node and modify the chain specification file before we start the next node. This ensures that every new node can find the previously started nodes in our network.

This is a (mostly) manual and tedious process.

Generating the enode and automating bootnode deployment

There is better way. The enode hash is derived from a private key which exists in the network/key file in the base_path of the node. If that file does not exist on startup, Parity will create one. However if it already exists Parity will calculate the enode from the private key in the file.

The idea is to create private keys for every bootnode and calculate the enode hash before starting nodes. We then insert all of enode urls in the chain specification file so that it contains all the bootnodes.

Before starting each node we place the private key in the $PARITY_BASE_DIR/network/key file and the chain specification file in $PARITY_BASE_DIR/chain.json. On startup the node should display the same enode hash we have calculated beforehand.

This makes it easy to mass deploy a group of boot nodes that can quickly find each other and to automate the process altogether.

Here is the code to create a private key and calculate the corresponding enode hash. Full credits go to Antoni Kedracki of Ethworks for providing me with this code snippet.

#!/usr/bin/env node
const ethers = require('ethers');

// Create a private key from which the enode hash will be derived
const privateKey = ethers.Wallet.createRandom().privateKey;
const noPrefix = privateKey.slice(2, privateKey.length);

// Print the network key that will generate the enode
// Paste this output into `$PARITY_BASE_DIR/network/key`
console.log(noPrefix);

// Print the enode hash
const signingKey = new ethers.SigningKey(privateKey);
var uncompressedPublicKey = ethers.SigningKey.getPublicKey(signingKey.publicKey, false);
const enode = `enode://${uncompressedPublicKey.slice(4, 132)}@<ip>:<port>`;
console.log(enode)