Bitcoin for beginners, Part 3: The BitCoinJ API

Build a Java-based Bitcoin transaction client

For Java developers, BitCoinJ is an entry point to developing applications that interact with the Bitcoin network. In this final article in a three-part series, Dirk Merkel helps you set up BitCoinJ in an Eclipse development environment, then walks through several short exercises that will familiarize you with this lightweight implementation of the Bitcoin transaction protocol.

Previous installments in this three-part series have introduced the conceptual and technological framework of Bitcoin, a virtual currency and peer-to-peer network. This article, a tutorial introduction to the BitCoinJ API, assumes that you are familiar with Bitcoin addresses, transactions, blocks, and the block chain.

BitCoinJ is an open source Java implementation of the Bitcoin protocol. As such, it's a handy tool to have if you want to write Java applications that interact with the Bitcoin network. In order to explore the BitCoinJ API, we'll construct various sample applications that illustrate the programming steps necessary to construct more complex Bitcoin applications in Java. After using Maven to built and set up a project in the Eclipse IDE, we'll practice creating a Bitcoin address, storing it in a wallet, and saving the wallet to disk. We'll then establish a connection to the Bitcoin test network and retrieve its genesis block. Finally, we'll tie together our sample code so far by sending some Bitcoins to an address on the test network.

About BitCoinJ

BitCoinJ is a Java implementation of the Bitcoin protocol. Written by Mike Hearn, BitCoinJ is not a full implementation of the original Bitcoin client, but a more lightweight and accessible version. While it's solid enough to learn from, BitCoinJ is still under development (currently at v.0.3) and should not be used to move large numbers of Bitcoins.

Get started with BitCoinJ

BitCoinJ is hosted by Google Code in a Subversion repository, and can be anonymously checked out. Once you check out the trunk of the BitCoinJ project you'll be able to easily keep it updated. You will not, however, be able to commit any changes.

You can use the Subversion client built into your favorite IDE or simply check out the project from the command-line, as I did:

Figure 1. Check out BitCoinJ from the command-line (click to enlarge)

Once you have the code, you'll compile it with Maven, BitCoinJ's build system. Maven takes a lifecycle approach to building projects and is highly extensible with many core and third-party plugins. What Maven does exceedingly well is manage dependencies. If you look at the Maven pom.xml file in BitCoinJ's root directory, you'll see that it uses only a handful of dependencies; these include JUnit and EasyMock for unit testing, SLF4J for logging, and the Bouncy Castle Crypto APIs for cryptographic operations such as hashing and signing.

From the command-line, run mvn clean package and Maven will retrieve these and other dependencies, compile the project, run the unit test suite, and package the compiled code into a snapshot JAR file. As shown in Figure 2, Maven first executes the clean lifecycle to get rid of any artifacts from previous builds. It then executes the phases of the default lifecycle up to and including the package phase.

Figure 2. Maven compiles BitCoinJ and its dependencies (click to enlarge)

Maven has a few more helpful tricks up its sleeve. First, executing mvn site:site builds the BitCoinJ documentation, including pages about dependencies, issue tracking, mailing lists, license, development team, source repository, and others. These pages tend to be informative but basic. Executing mvn javadoc:javadoc generates the project's documentation, which will come in handy when we start to exercise the BitCoinJ API.

The documentation reveals that the API is divided into four packages:

  • Discovery deals with peer-to-peer network discovery/communication.
  • Store contains data structures for storing blocks and the block chain.
  • Examples includes a handful of simple applications based on BitCoinJ (these inspired my own examples for this article).
  • Core contains the majority of BitCoinJ's classes and functionality, including classes to communicate with peer nodes, download the block chain, and send and receive transactions.

Set up the example project in Eclipse

We'll develop the example code for this article in Eclipse, using Maven to manage BitCoinJ as a dependency. Fortunately, BitCoinJ has a continuous integration environment that builds the project, collects and reports on various artifacts, and deposits a snapshot JAR into the project's own Nexus-based Maven repository.

Figure 3 shows the Eclipse project-creation dialog that results from creating a new Maven project and selecting the "quickstart" archetype, which generates a basic Maven project. My code for this project lives in a package named com.waferthin.bitcoinj, which produces a 0.0.1-SNAPSHOT with the Maven build.

Figure 3. Creating a Maven project in Eclipse (click to enlarge)

Clicking Finish instructs the wizard to create the project, which means dropping a "Hello World" main class into the project directory -- named src/main/java/com/waferthin/bitcoinj in my case.

Finally, we need to tell Maven that the project depends on the BitCoinJ snapshot, as shown in Listing 1. I edited Maven's wizard-generated pom.xml file to declare the location and name of BitCoinJ's Nexus repository (lines 18 through 28) and set the version to depend on for the build (lines 39 through 45):

Listing 1. Maven pom.xm for the BitCoinJ project

001|<project xmlns="" xmlns:xsi=""
002|  <modelVersion>4.0.0</modelVersion>
004|  <groupId>com.waferthin.bitcoinj.explored</groupId>
005|  <artifactId>bitcoinj-explored</artifactId>
006|  <version>0.0.1-SNAPSHOT</version>
007|  <packaging>jar</packaging>
009|  <name>bitcoinj-explored</name>
010|  <url></url>
012|  <properties>
013|    <>UTF-8</>
014|  </properties>
016|  <repositories>
017|    <!-- declare BitCoinJ repository name and location -->
018|    <repository>
019|      <id>bitcoinj-release</id>
020|      <releases/>
021|      022|<url></url>
023|    </repository>
024|    <repository>
025|      <id>bitcoinj-snapshot</id>
026|      <snapshots/>
027|      <url></url>
028|    </repository>
029|  </repositories>
031|  <dependencies>
032|    <dependency>
033|      <groupId>junit</groupId>
034|      <artifactId>junit</artifactId>
035|      <version>3.8.1</version>
036|      <scope>test</scope>
037|    </dependency>
039|    <!-- declare BitCoinJ dependency w/ version -->
040|    <dependency>
041|      <groupId></groupId>
042|      <artifactId>bitcoinj</artifactId>
043|      <version>0.3-SNAPSHOT</version>
044|      <scope>compile</scope>
045|    </dependency>
046|  </dependencies>

That's all there is to it. In the next section we'll import the BitCoinJ classes into our code and build a BitCoinJ project with Maven, all without having to copy the actual JAR file.

Creating a Bitcoin address

To send or receive Bitcoins, you need an address. Addresses are derived from the public portion of a public-private cryptographic key pair (see "Bitcoin for beginners, Part 2: Bitcoin as a technology and network"). The kind of cryptography used by Bitcoin is called elliptic curve cryptography (ECC). The public-key cryptography most of us know is based on the difficulty of finding the prime factors of large integers. In contrast, ECC is based on the difficulty of finding the discrete logarithm of an elliptic curve. (Explaining this in more detail would not only lead us down the rabbit-hole of higher algebra, but would also quickly exceed my college math. Fortunately, we don't need to know more in order to use BitCoinJ's ECKey class to represent and generate key pairs.)

In line 20 of Listing 2, we create a new elliptic curve key pair by instantiating an object of type ECKey. Note that the class's default toString() method is overwritten to return the public and private key in hex notation, which is used on line 23.

Listing 2. Creating an elliptic curve key pair with ECKey

001|package com.waferthin.bitcoinj;
007|public class CreateAddress {
009|    public static void main(String[] args) throws Exception {
011|        // use test net by default
012|        String net = "test";
014|        if (args.length >= 1 && (args[0].equals("test") || args[0].equals("prod"))) {
015|            net = args[0];
016|            System.out.println("Using " + net + " network.");
017|        }
019|        // create a new EC Key ...
020|        ECKey key = new ECKey();
022|        // ... and look at the key pair
023|        System.out.println("We created key:\n" + key);
025|        // either test or production net are possible
026|        final NetworkParameters netParams;
028|        if (net.equals("prod")) {
029|            netParams = NetworkParameters.prodNet();
030|        } else {
031|            netParams = NetworkParameters.testNet();
032|        }
034|        // get valid Bitcoin address from public key
035|        Address addressFromKey = key.toAddress(netParams);
037|        System.out.println("On the " + net + " network, we can use this address:\n" + addressFromKey);
038|    }

You might recall that the public part of a Bitcoin key pair should be an address. But the public part of the key generated by the above code will initially look nothing like the addresses the Bitcoin client displays in its UI. The address form we're used to seeing in a Bitcoin transaction is derived by repeated hash operations to the public key. This form includes a flag that indicates which of the two Bitcoin networks the key belongs to -- Bitcoin's production network or its test network. (See the Bitcoin wiki page for a more detailed description of the algorithmic creation of Bitcoin key pairs.)

Differentiating Bitcoin networks

Currently there are two Bitcoin networks, one for production and one that is used for development. Both networks have their own genesis block and subsequent block chain. Later in this article, we'll use the Bitcoin testnet to execute a Bitcoin transaction. For now, you only need to know that the networks are differentiated by pre-pending a single byte to the input to one of the cryptographic hashes in the ECC algorithm: 0x6f indicates the production network and 0x00 the test one.

We don't need to apply the sequence of cryptographic hashes ourselves because the ECKey class provides the same functionality with the toAddress() method. After invoking that method and passing in the type of network via a NetworkParameters object (see line 26 in Listing 2), the toAddress() method returns an Address object. That object's toString() method will yield a true Bitcoin address. After compiling and executing the class I get the following address for Bitcoin's test network:


Testnet addresses typically start with m or n, whereas production addresses start with 1. Try executing the same code on your own machine and you will get a different, unique address.

Wallets and keys

If you participate in the Bitcoin economy, you likely keep all of your riches in your wallet. The wallet is nothing more than a local data file that contains serialized objects representing all of your Bitcoin transactions and a cache of unused addresses. The sum of your incoming and outgoing transaction amounts is the amount of Bitcoins in your wallet. In this section we'll use BitCoinJ's Wallet object to create a wallet data file, populate it with five addresses, and save it to disk.

The Wallet class implements the Serializable interface to enable us to persist it to disk or some other more permanent storage medium. Specifically, methods loadFromFile(File) and the corresponding saveToFile(File) read and write wallet files. We'll be using loadFromFile(File) to write a newly created wallet object to a file.

Note that BitCoinJ wallet files are not compatible with wallet files created by the official Bitcoin client.

Creating and storing keys

The Wallet class has a public member named keychain that is an ArrayList of type ECKey, which is used to store all EC key pairs in the wallet. The addKey(ECKey) method is used to add key pairs, but there is currently no method for removing them. This makes sense because it shouldn't be easy for users or programs to delete private keys: a private key is required to access funds sent via its corresponding public key. Without a key pair in the wallet or backed up somewhere, any sent funds would be lost forever.

1 2 3 Page 1
Page 1 of 3
How to choose a low-code development platform