How To Setup A Custom Ethereum Testnet
June 18, 2020
Continuing the series on hosting your own testnets, I'll go into details on how to setup a custom Ethereum testnet and why you would want to do that in the first place. If you haven't read the previous posts and you're interested in the topic, you can check out previous blogposts on How to setup custom Bitcoin testnet and How to set up EOS testnet.
Complete example is available on our GitHub.
Ethereum testnets each developer need to know: Ropsten, Rinkeby and others
Ethereum has 5 different testnets, named after subway stations Ropsten, Kovan, Rinkeby, Sokol and Görli. Each of these networks have different rules and consensus protocols - methods for achieving agreement between users on the network on which block should be included in the blockchain. Ropsten uses proof-of-work, which makes it the closest one to mainnet. Miners are required to complete mathematical puzzles, which are then verified by other users on the network. This also makes it the most unstable one. Did I already mention that testnet coins are worthless? There is no financial incentive for people to mine Ropsten coins. Just look at this chart:
At the time of writing this post, 15 miners were generating testnet coins in the last 24 hours, while the majority of blocks were mined by just 1 miner! PoW networks are only as secure as the computing capacity behind it. It would be really easy to execute the infamous 51% attack.
There are just a handful of miners doing the work, making it susceptible to the infamous 51% attack. At the time of writing this post, just 15 miners were interacting with the network within the last 24 hours. And this is what happened in 2017 - some malicious actors inflated the block gas limit to 9 billion, followed by sending gigantic transactions, crippling the entire network in the process. It was later revived through a soft-fork which placed a hard limit on gas, making this specific attack impossible. It can still be abused, just not in this specific way.
Other networks use proof-of-authority protocols, such as Aura and Clique. You could call them public-permissioned blockchains. In these networks, certain nodes are given permission to participate in the mining process. It's public in a sense that everyone can connect to the network, execute transactions and read the data stored in the ledger, but only a handful of users can mine blocks. Since you can't participate in the mining process, the only way to obtain coins on the network is through a faucet. The fact that there are different protocols, certain networks work only with Parity or Geth. Görli exists for this exact reason. It was specifically designed to be compatible with most clients.
Why should you host your own testnet?
You might wonder, since there are so many choices out there, why should you bother setting up your own then? We chose this approach for similar reasons as with other currencies.
- Persistence - there is no guarantee that any of the public testnets won't be reset. This is how Ropsten was born, the previous testnet called Morden was simply too hard to use due to all the junk data, since syncing took a long time.
- Control - our use case requires us to have control over a large sum of funds. If you read my previous posts you probably already know how I feel about faucets. Don't get me wrong, they're great, it just doesn't work for us. Hosting a custom testnet gives us the most control over where the coins are and when new blocks are mined.
- Storage requirements - participating in any of these networks requires you to sync up with them. We can significantly lower the storage requirements by simply rolling our own and not bothering with the data we don't need.
Tools to setup custom Ethereum testnet
Our setup will consist of three services:
- Single Geth node,
We chose the Clique proof-of-authority consensus protocol, since it perfectly fits our use case. This method does not rely on miners being able to solve complex puzzles but instead it gives certain users permission to act as block validators, also called signers. As long as the majority of signers agree that the block is valid, it will be included in the blockchain. They can vote on either approving or kicking out accounts from the group which gives all of them incentive to behave nicely on the network. Blocks are mined with a fixed rate which we can configure, making sure our transactions are confirmed fairly quickly.
Geth node will serve as a signer and provide RPC API for our users. There is a caveat here - this unfortunately means that everyone will be able to interact with the blockchain as our signer. Since this will be an internal testnet, we can assume that our users will not actively try to break things. We’ll also limit the attack surface by not exposing any sensitive APIs such as ones that allow submitting blockchain transactions. Just bear in mind that technically we’re trading security for simplicity here and even though it works for us, it might not suit your use case.
For an explorer app we went with Blockscout. It’s open-source, fairly easy to set up and works well. It uses a PostgreSQL database for storage.
Setting up the Ethereum testnet
We'll be using Docker to set up a custom testnet. Our network will use two accounts. Buffer, which initially holds all the coins and signer which is used by Geth node to participate in the mining process. Once we generate the keys to these accounts, we will create a genesis.json file describing the initial state of our blockchain and bootstrap it using Docker Compose.
Creating the accounts
To create the accounts we'll use vanity-eth.tk tool. It allows us to quickly generate any key pair we want through a simple web interface. You can leave the default settings and simply press the Generate button.
Generate two key pairs and note them down somewhere, we'll need them later. As an example, I'll use these:
A file which describes the initial state of our testnet is called genesis.json. First block on our blockchain will be based on the contents of this file. Let's go through all the sections and explain what is happening here.
Config section describes core blockchain settings. What's interesting about it is that this data is not a part of the genesis block. Any network identified by a given block can have entirely different set of configuration options.
The remaining options tell the client when certain hardforks to the Ethereum networks occur. These are essentially protocol changes which break compatibility and are usually scheduled ahead of time so that all clients on the network have time to adjust accordingly. Once a certain block height is passed, these changes come into effect.
Not including the field means that our network does not use the fork. We chose to go with zeros, meaning all the protocol changes are already applied. You can use the Geth source code as reference.
Data is expected to be in a specific format:
This section describes initial balances of accounts on the network. We chose to allocate all the coins to our buffer account.
Remaining options describe our genesis block. These are pretty much self-explanatory.
Save your complete genesis.json file in your work directory.
Bootstrapping the network
Once you're done, you can bootstrap the network using Docker Compose.
Your testnet should be up and running in a couple of seconds, which you can confirm by navigating to http://localhost:4000.
As you can see, new blocks are mined every minute.
Making it rain 💰💸
Now that the network is up, we can configure MyEtherWallet to connect to it and submit our first transaction. Let's navigate to MyEtherWallet.com and use our buffer accounts private key to access the wallet with all the coins. Choose Access My Wallet, then Software and Private key.
It should look like this:
After clicking Save, scroll to the bottom of the list and you should see your local testnet under the Custom Networks section. Clicking on it will make MEW connect to your local Geth node. If everything went well, you should see this screen:
We are rich!
To make things easier for your users, you might want to export MyEtherWallet configuration to a JSON file. This way they won't have to deal with the form from the previous step. This is especially important once you have some ERC20 tokens on your network as users will be required to enter contract address, token symbol and decimals for each token. Providing a JSON file saves them the trouble of configuring everything by themselves, since all they'd have to do is import it.