Quick guide to generating addresses with NovaCrypto

Alan Evans
4 min readDec 13, 2017

--

In this guide I’m going to show you how to use NovaCrypto’s libraries to generate a wallet and derive some of the addresses and private keys of that library. I assume you have some knowledge of mnemonics, public addresses and private keys, but maybe you’ve never derived them yourself in code.

Note: The BIP32 library of NovaCrypto is in review and testing, so use at own risk for money. Feedback on any of the libraries would be greatly appreciated.

Start a new Java, Kotlin or Android project. A gradle Java command line project is assumed for examples and dependencies, but pom should work too. All NovaCrypto projects are distributed via jcenter, so make sure you add that repository.

IntelliJ create new project
repositories {
jcenter()
}

Creating a new wallet mnemonic

First add a new dependency on https://novacrypto.github.io/BIP39/

compile 'io.github.novacrypto:BIP39:2018.10.06'
Sync it

Sync IntelliJ with gradle.

In our main class and method, add these lines:

package com.example;

import io.github.novacrypto.bip39.MnemonicGenerator;
import io.github.novacrypto.bip39.Words;
import io.github.novacrypto.bip39.wordlists.English;

import java.security.SecureRandom;

public final class Main {

public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
byte[] entropy = new byte[Words.TWELVE.byteLength()];
new SecureRandom().nextBytes(entropy);
new MnemonicGenerator(English.INSTANCE)
.createMnemonic(entropy, sb::append);
System.out.println(sb.toString());
}
}

Run it, you will see a 12 word mnemonic like below:

patrol scrub ring nothing circle answer arrange spirit spider skirt field adult

Each time you run it, you will generate a new mnemonic due to the SecureRandom being the source of the entropy.

Let’s refactor:

public static void main(String[] args) {
final String mnemonic = generateNewMnemonic(Words.TWELVE);
System.out.println(mnemonic);
}

private static String generateNewMnemonic(Words wordCount) {
StringBuilder sb = new StringBuilder();
byte[] entropy = new byte[wordCount.byteLength()];
new SecureRandom().nextBytes(entropy);
new MnemonicGenerator(English.INSTANCE)
.createMnemonic(entropy, sb::append);
return sb.toString();
}

Why doesn’t such a method exist in the BIP39 library?

I didn’t want to suggest a way of generating the entropy, and that’s not covered by the BIP39 spec so falls outside of the responsibility of the library.

Mnemonic -> Seed

Before we can do anything interesting with keys and addresses we need a seed, this is a one-liner:

final byte[] seed = new SeedCalculator().calculateSeed(mnemonic, "");

Note that I’ve used an empty string as a passphrase, you can use any string, each string will generate a different wallet. Note that devices such as ledgers don’t allow passphrases.

Seed-> Root key

New dependency:

compile 'io.github.novacrypto:BIP32:2018.10.06'

Let’s find the root key for this wallet, the network is only for serialization purposes, which includes producing an address.

PrivateKey root = PrivateKey.fromSeed(seed, Bitcoin.TEST_NET);

We can now create addresses, there are a few ways for us to do this:

String addressMethod1 = root
.cKDpriv(hard(44)) //fixed
.cKDpriv(hard(1)) //bitcoin testnet coin
.cKDpriv(hard(0)) //account =1
.cKDpriv(0) //external
.cKDpriv(0) //first address
.neuter().p2pkhAddress();

String addressMethod2 = root
.cKDpriv(hard(44)) //fixed
.cKDpriv(hard(1)) //bitcoin testnet coin
.cKDpriv(hard(0)) //account =1
.neuter() //switch to public keys
.cKDpub(0) //external
.cKDpub(0) //first address
.p2pkhAddress();

String addressMethod3 = root
.derive("m/44'/1'/0'/0/0")
.neuter().p2pkhAddress();

System.out.println(addressMethod1);
System.out.println(addressMethod2);
System.out.println(addressMethod3);

All 3 return the same:

naive sudden open ostrich tail force duty autumn aim saddle matrix cheese
mwXNJnWnj336bFxAg3KhkoV5NatuFSrLXr
mwXNJnWnj336bFxAg3KhkoV5NatuFSrLXr
mwXNJnWnj336bFxAg3KhkoV5NatuFSrLXr

Root key -> address via BIP44 classes

New dependency:

compile 'io.github.novacrypto:BIP44:2018.10.06'

This gives us another way to derive addresses:

AddressIndex addressIndex = m()
.purpose44()
.coinType(1)
.account(0)
.external()
.address(0);
String addressMethod4 = root.derive(
addressIndex,
AddressIndex.DERIVATION
)
.neuter()
.p2pkhAddress();

This allows us to have a fluent API.

It also allows us to split the derivation in to two parts nicely:

Account account =
m().purpose44()
.coinType(1)
.account(0);
ExtendedPublicKey accountKey = root.derive(
account,
Account.DERIVATION
).neuter();

String addressMethod4 = accountKey.derive(
account.external().address(0),
AddressIndex.DERIVATION_FROM_ACCOUNT
).p2pkhAddress();

This allows us to derive the public keys (and addresses) in a strongly typed way. Let’s print the first 20:

final Account account =
m().purpose44()
.coinType(1)
.account(0);
final ExtendedPublicKey accountKey = root.derive(
account,
Account.DERIVATION
).neuter();

final Change external = account.external();

for (int i = 0; i < 20; i++) {
final AddressIndex derivationPath = external.address(i);
final ExtendedPublicKey publicKey =
accountKey.derive(
derivationPath,
AddressIndex.DERIVATION_FROM_ACCOUNT
);
System.out.println(
derivationPath + " = " + publicKey.p2pkhAddress()
);
}

Results in:

motion clever argue fever suffer point energy alpha target quality engine hip
m/44'/1'/0'/0/0 = mm8GhZHP6nXZRWN1aq36yhUeVwiRk6iaHk
m/44'/1'/0'/0/1 = mx2df8FXnSLq1dawGPBzxRiR19Ud8WWuuC
m/44'/1'/0'/0/2 = mjdKtJztn9Zj4mC3UiU8NvskK6qDS4hnfW
m/44'/1'/0'/0/3 = mpfgYuBgeRDvqZrNnucsGcwCVEiQgJ5wtw
m/44'/1'/0'/0/4 = mpXLQHx4bbcCroRHcSJtytitGhz97x4KhV
m/44'/1'/0'/0/5 = my934xbTqCnJdVubSm26rpA4X2FZVry24m
m/44'/1'/0'/0/6 = mnoview9mm5yVQCPPoCUAcPQ2b76kJbRdw
m/44'/1'/0'/0/7 = n2kbxm7ve5QmYnZVL9ZwbcPfTvFBgPDbzh
m/44'/1'/0'/0/8 = mpW8BXisz8xDSV9RaoYrFH2grsPNHY379S
m/44'/1'/0'/0/9 = mmyT2tSbwFnhL2ayFQedjc9dmj96Tkw96Q
m/44'/1'/0'/0/10 = mmtgcmeai7m1SyzyVPED2vKSTCydCiCufg
m/44'/1'/0'/0/11 = mtjVwGmnERqeU9QAy1pu89XGbezyZU1cMK
m/44'/1'/0'/0/12 = mvgBuc9tuEZy9B5oddLBV9tCMFdKKvM2HT
m/44'/1'/0'/0/13 = mggQjZRTL3mGsqaadYGP1Ze4yxqXaVW46t
m/44'/1'/0'/0/14 = n18oNtgYQC2hFPhKzJeEahofVWvTiXTwwa
m/44'/1'/0'/0/15 = mzUyFruURgvk2bwAMewj4RMoeLm3vdFYfN
m/44'/1'/0'/0/16 = mtVM3pgqX7itgVmMUAuD422tGA6j7wKHXi
m/44'/1'/0'/0/17 = mhCFDko26zS6WgbTNiiPLGmyvqpZM4hmc3
m/44'/1'/0'/0/18 = mkfz9zxJxpPk3MkrKHZdLLaHwgoZdBtHtA
m/44'/1'/0'/0/19 = mg3X6vXXHqogLvRXCQ1bwuWYiJtbCPJVWE

Place that mnemonic in https://iancoleman.io/bip39/ select Bitcoin test net as the coin type, and you will see the same addresses.

All the code for this example is here:

--

--

Alan Evans

British Canadian Software Developer living in Connecticut, Staff Android Engineer at The New York Times