Source: https://dzone.com/articles/bitcoin-and-junit
Bitcoin and JUnit
Here's a solid guide to help you test your Bitcoin-centric projects with JUnit. See what it takes to set up and use a proper test environment.
Bitcoin and blockchain are hot topics today. Many related projects are already out there and much more are being developed right now. If you are a developer in this area, then you know how important and tricky it is to have bulletproof testing in place. In this article, I will briefly describe the options for testing Bitcoin and then go into more detail of the choice that you can run easily and offline. Please note that this article is focusing purely on Bitcoin.
Testing Options
There are three possible modes or stages for testing Bitcoin applications.
- Regtest mode
- Test on the testnet
- Test on the mainnet
Regtest mode is what I would recommend starting with. It allows you to run the node on your local computer just as a sandbox, completely isolated from the internet. And it has one very useful feature. You are fully in control of the block generation, and new blocks can be 'mined' just by calling a simple command. This removes the need to wait for blocks to be mined. Also, you have an unlimited number of coins to play with because all the mining awards go to the account on your local node.
Testnet, as the name suggests, is a place that behaves almost the same as the real Bitcoin network. It is a fully functional network. This includes real mining, necessary waiting time, and a need to account for the activity of other people. The differences are that coins don't have any value, anyone can get a small number of free test coins, and the whole network gets nuked from time to time. It's a good next step after having a working system on the regtest.
Finally, mainnet is the network where the real transactions are happening. Bugs can become very expensive here, so it is better to leave this for the final test, after being confident that everything is working as planned.
I believe that during application development, you should walk through all three stages. The rest of this article will show how to connect regtest mode with JUnit. First, let's just get a little bit familiar with bitcoind, a program that implements the Bitcoin protocol and can act as a node.
Start with software installation. I prefer Bitcoin Unlimited. Download the latest version of the Official Bitcoin (BTC) Release for your platform and unzip/install it. Open the console and navigate to the folder with your binaries. A local node in the regtest mode can be started with the following command.
bitcoind -regtest -txindex=1 -server -rpcallowip=127.0.0.1 -rpcport=18332 -rpcuser=user -rpcpassword=Password1
There are many parameters you can put into the command, and there is a reference guide here. The important ones are those that specify regtest mode, data directories, and open JSON-RPC ports for clients.
The next step is to open a second console, then navigate to the same directory, and use JSON-RPC client to perform actions. The following sequence generates blocks, sends coins to the specified address, and generates next set of blocks to simulate transaction progress in the chain.
bitcoin-cli -regtest -rpcport=18332 -rpcuser=user -rpcpassword=Password1 generate 101
bitcoin-cli -regtest -rpcport=18332 -rpcuser=user -rpcpassword=Password1 getbalance
bitcoin-cli -regtest -rpcport=18332 -rpcuser=user -rpcpassword=Password1 sendtoaddress put_your_address_here 10
bitcoin-cli -regtest -rpcport=18332 -rpcuser=user -rpcpassword=Password1 generate 7
If you have made it up to here, then you should be able to test your application and restart your chain from the beginning (by deleting the data directory) whenever you want. For the moment, though, that's all done manually. The next chapter will show you how to automate this.
An example project is available on the original post. It's a Java Maven project that contains a single unit test and that can be invoked from the command line just by mvn test
. Bitcoin binaries for Windows are included. If you are using different platform, then please download and replace the binaries.
Coding is pretty straightforward and can be summarized in the following points:
- Clean up the data directory and start the bitcoind process inside the test setup.
- The tested client connects to the node during the test case as needed.
- New blocks are generated on demand (there is a method for that).
- The bitcoind process is stopped during the test teardown.
Note: Depending on your environment, you might need to deal with two issues — permissions and firewall.
Here is how you start the new bitcoind process.
ProcessBuilder processBuilder = new ProcessBuilder(BIN_DIR_PATH + "/bitcoind.exe", "-regtest",
"-datadir=" + dataDir.getAbsolutePath(), "-txindex=1", "-server",
"-rpcallowip=127.0.0.1", "-rpcport=18332", "-rpcuser=user", "-rpcpassword=Password1");
processBuilder.directory(new File("src/test/resources/bitcoinUnlimited-1.0.3/bin"));
try {
bcProcess = processBuilder.start();
Thread.sleep(5000);
} catch (IOException e) {
throw new RuntimeException("error during process start", e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
The variable bcProcess
is defined in the test class and is used in a teardown method to close the process. The 5-second thread sleep is trickier. processBuilder.start()
returns
immediately when the process is started. Unfortunately, the
bitcoind process is not initialized at that point, so the connection
would fail.
Next, how to stop the process.
try {
bcProcess.getInputStream().close();
bcProcess.getErrorStream().close();
bcProcess.getOutputStream().close();
bcProcess.destroy();
bcProcess = null;
Thread.sleep(1000);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
That's the whole "magic". The rest of the code just cleans up the working directory before the test and invokes the actual test. For more details, please look at the source code.
Summary
As you can see, the Java code to run and interact with Bitcoin nodes is easy and it works. You can write test cases that run relatively fast and have a guaranteed environment. The portion that I don't really like is the dependency on the native program — especially while imagining handling multiple chains on the multiple systems. Then the number of necessary binaries can grow significantly. I have a couple of ways to resolve that in mind, but please let me know if you have an elegant solution for that.