Table of Contents:
Creating a Starter Project for hello-money
Now type the following to have Maven create an empty starter project:
These commands create a directory called hello-money at the current location with the hello-money starter project.
If Maven prompts you for answers during this process, just keep pressing enter to use the default setting.
Next, we need to inform Maven that this program will use a couple of external libraries. We do this by editing the file pom.xml, which should now exist in the new directory. Also in this file should be a section named <dependencies> where we'll add bitcoinJ as a new dependency. After the previous dependency (i.e., after the line that reads </dependency> singular), add the following:
Now we'll add a plug-in called exec-maven-plugin to our program. A plug-in is a special type of library. The exec-maven-plugin will make it easier to run our finished program from the command line.
At the very bottom of the projects in pom.xml (i.e., after the line that reads </dependencies> plural), add the following lines:
Now we're ready to run this empty program for the first time as a test. To do this, execute the following lines from the console in the program's directory:
The first line © loads all the necessary libraries into the project and builds the program into a Java bytecode file. The second line 0 actually runs the program.
If the program runs successfully, you should see Hello World! printed on the screen. This means Maven has successfully created a functioning Java program and that we're now ready to start writing the core Bitcoin code.
Writing the Code for hello-money
The entirety of the code for our hello-money program follows. To add this to the project, open the file src/main/java/hellomoney/App.java and replace its contents with this program:
Next, run the command mvn install, which checks the syntax of this new program code and builds it into a program file. If the build works, the message BUILD SUCCESS should appear (along with tons of other esoteric messages).
Before we run the program, let's walk through it step-by-step to see what it does.
Declarations at the Top of the Program
The first line in the program declares the name of the package:
Next, we declare all the libraries the program will reference:
Three of these references are for Bitcoin classes: First, we'll use the core libraries to access basic Bitcoin classes (such as classes for wallets and keys). Second, we need classes for storing the blockchain (called a block store in BitcoinJ lingo). Third, we need to use the DnsDiscovery class, which helps us find other nodes participating in the Bitcoin network. We import the java.io.File class because we'll be writing our block store to a file, and we import the java.math.Biglnteger class to work with, well, big integers.
Now let's define the Java class that holds the program:
The program code is stored in a new class called App, which contains a single member function main. We mentioned this hellomoney.App class in the pom.xml file, declaring it as the main class of the program.
Let's look at individual lines in the main function.
Initializing Our Java Objects
Here is the code that initializes the Java object we need from the bitcoinJ library.
We start by fetching the network parameters for the main, production Bitcoin network O. Although only one true Bitcoin network is actually used for transactions, it's difficult to test the Bitcoin system thoroughly with real money; therefore, Bitcoin developers also maintain a second Bitcoin
network called TestNet for testing only. The NetworkParameters structure contains information about the genesis block (the first block in the block-chain) as well as information about the maximum number of coins and several other assorted details, which may differ between the main Bitcoin network and the TestNet. By having all this information packed in the NetworkParameters data structure, it's easy to connect our program to another network besides the main Bitcoin network, such as the TestNet, as desired.
Next, we create a new, empty wallet that we'll set up to receive our coins 0. As discussed earlier, Bitcoin wallets contain one or more Bitcoin addresses, and each Bitcoin address consists of a public and a private key. Here ©, the bitcoinJ library creates a new key pair for us. Then, we print out the public address and private keys that were generated 00. Finally, we add our new key pair to the wallet ©.
Usually, when using bitcoinJ, you should reuse the same wallet every time the program runs and load/save it every time the program starts/stops or your program can lose track of money. This is not an issue for the simple hello-money program. However, before you build more sophisticated bitcoinJ programs, read "Gotchas When Using Wallets in BitcoinJ" on page 239.
Not only does a Bitcoin app need a wallet, it also needs a blockchain. The following lines initialize a new blockchain for us:
Because blockchains consume lots of space, we'll write it to a file named my-blockchain ©. Next, we create a block store, which is an object that manages the data for our copious blockchain data 0. BitcoinJ offers several block store types, all with different feature and performance trade-offs. In this example, we'll use an SPVBlockStore object, which is usually the best choice for most apps.
So what are the trade-offs you need to know about? Well, the biggest performance challenge any app that works with bitcoins has to deal with is that the official Bitcoin blockchain is larger than 10GB in size. Do most Bitcoin apps really need all 10GB of the blockchain?
To answer this question, let's consider why the blockchain exists. At a simplified level, a Bitcoin blockchain is responsible for two main jobs:
1. Figuring out how much money everyone on the network has
2. Figuring out whether new transactions broadcast across the network are valid
For the first task, the blockchain allows us to examine all the historical blocks in the blockchain and compile comprehensive data about every Bitcoin address ever used and how much money each contains. For the second task, it allows us to examine new blocks of transactions created by the network and then to verify that these blocks have the appropriate hashing information that proves they are correctly mined blocks according to the latest difficulty requirements.
But consider the first job of the blockchain: Do most apps need to discern how much money is in every wallet in existence? No, most apps only need to identify the amount of money in one or a small number of wallets. Therefore, not all 10GB of data are needed. The prescient Satoshi, in his original Bitcoin whitepaper, was able to see that in this case, an optimization called Simplified Payment Verification (SPV) was possible.
We also covered SPV a bit in Chapter 9, when comparing different types of Bitcoin wallets.
Here's a quick refresher of how SPV works: If you know you're interested in a single wallet ahead of time, you can just tally up how much money is in that one wallet as you pull the entire historical blockchain off the Bitcoin network. At that point, you only need to store header information of blocks and can ignore information in older blocks entirely in most situations, which is what SPVBlockStore does. In doing so, the SPVBlockStore (as of 2014) is less than 1GB in size, less than a one-tenth the size of the official blockchain, and this is why we use SPVBlockChain to store our data.
Once we've created the block store, we can use it to create a Blockchain object ©. Notice that when we create this Blockchain object, we must pass in our wallet as created. Because we're not downloading all 10GB, the block-chain object needs to know ahead of time which wallets (and their addresses) are important to us so it can select the right blockchain data to download.
Even though SPVBlockStore is much smaller than the full blockchain, it can still take a long time for your app to download all the needed data from the network—usually, about 20 minutes. However, it will write this data to a file, and an SPVBlockStore object is smart enough to check the supplied file to see whether any data has already been downloaded since the last time the program was run. If so, it downloads only new data that has arrived after the program was last run.