Building a Mnemonic Generator in Java Using BIP-39 Wordlist
Hi all. Today I want to show how you can implement your own realization of mnemonic generation using BIP-39 Vocabulary. Some time ago I tried to create my own game using Blockchain standards and the first thing I should realize is mnemonic generation.
Today I want to show you a simple example with diagrams and source code, which can give you some knowledge in this field. I don't position myself as professional, but it’s enough for this article.
First, BIP-39 Vocabulary contains 2048 unique words and presents them as a list. Before starting you can download Vocabulary from the GitHub Repository link. I also get the wordlist from here. Within this article, I will create a mnemonic generator for 12 words, but you can expand it to 15/18/21/24.
Let’s start with theoretical information. For a generation, we need to get random information, referring to which we will get information to generate a new mnemonic. For this goal, I will use timestamps with type long and multiply that using random numbers from double format using Math.random(). This operation will repeat the n-count of iterations. After getting a random string we will be available to start our generation process.
- Starter. The starter is an auto-generated Main class by IntelijIdea in my case. (I don’t use Maven or something else for this project.). The starter contains all of the services as final static fields and is used during the whole process. The Starter will be simple because all necessary logic is shared between services.
- WordsStock Value Object. Contains only one int field that is used to indicate the amount of characters in the index. In Vocabulary we have only 4 groups of indexes: [0;9], [10;99], [100;999], [1000,9999] (yes, the maximum is 2048, but for presentation, I wrote 9999). So, in the first group amount of characters in the index equals 1. In the second group, amount equals 2. In third equals 3. In fourth equals 4. I hope you know me on this step.
- WordsStockService. Used to control the List of WordsStock. It’s very simple method to control WordsStock Value Objects and check which indexes will already committed to mnemonic during the generation process.
- VocabularyService. Used to load all words from Vocabulary to List of Strings. If you use something like Spring, you can easily load Vocabulary when an application is running, but in this case, loading will initialize when we create an instance of Service in the Starter class.
- StringService. A significant part of our magic will stay here. This Service is used to generate random Strings. I will describe this below.
- MnemonicService. Contains only a Set of Strings and some methods to modify the Set.
Firstly, we need to get all the words from the Vocabulary, right? So, let’s start with VocabularyService. Create it on your project and set the path to Vocabulary in a final variable. In my case, I used the absolute path to the file, but you can simply modify that and use a relative path, if you move the file to the resources folder, for example.
After that, we need to define the constructor for the Service and read each line of a file. One line = one word. As a result, we will have a List of Strings with all words. For a reader, I used the default Java scanner.
private final String ABSOLUTE_FILE_PATH = "/Users/user/Desktop/english.txt";
private final List<String> words = new ArrayList<>();
public VocabularyService() {
try (BufferedReader reader = new BufferedReader(new FileReader(ABSOLUTE_FILE_PATH))) {
String line;
while ((line = reader.readLine()) != null) {
words.add(line);
}
} catch (IOException e) {
System.err.println("Error while file reading: " + e.getMessage());
}
}If a file does not exist or something went wrong file read process, an application will shutdown with an error message.
After that, we need to create a WordsStock class, that is used to define our indexes combination. Four groups, right?
public class WordsStock {
private int charactersInWord;
public WordsStock(int charactersInWord) {
this.charactersInWord = charactersInWord;
}
public int getCharactersInWord() {
return charactersInWord;
}
}An auto-generated getter will be needed in the future in the generation process.
Another service needs to save and modify our future mnemonic. As I said, it will be a Set of Strings, where each String is a word of mnemonic.
public class MnemonicService {
Set<String> mnemonic = new HashSet<>();
public void addWordToMnemonic(String word) {
mnemonic.add(word);
}
public String getMnemonic() {
return mnemonic.toString();
}
}Let’s discuss why I use Set and not List or something else. If we add a word that will already added to the mnemonic Set, this word will not duplicate in the Set. At the same time, if I use List and during the generation process I’m trying to add a word, that duplicate in mnemonic, it will be wrong. Yeah, that concept needs to be realized by the validation method after generation to check if the Set consists of 12 words or not because we have some chance of duplicating. Note: validate method will not be shown in this article, but you can easily add it to the relevant Service.
Duplication problem has a minimal chance, but it’s can be a big problem. I have two approaches to generation mnemonic, but the approach in this article doesn’t provide for that. Maybe in the future, I can show you another way to realize also with Set.
And now we can go to more interesting aspects of this article. WordsStockService. As I said above, we have 4 groups of indexes. So, we need to define that in the constructor of Service.
public static final int LIMIT = 3;
private final List<WordsStock> globalStock = new ArrayList<>();
/*
12 words:
3 words - 1 char
3 words - 2 char
3 words - 3 char
3 words - 4 char
*/
public WordsStockService() {
globalStock.add(new WordsStock(1));
globalStock.add(new WordsStock(2));
globalStock.add(new WordsStock(3));
globalStock.add(new WordsStock(4));
}Now when I create an instance of this Service in Starter, this Service will automatically contain the List with 4 groups named as WordsStock Value Objects. Constant LIMIT is used to define the amount of words from each group. 4*3 = 12 is a needed amount of words for your mnemonic. But we need two more methods in this Service for our future generation process.
public int getGlobalStockCount() {
return globalStock.size();
}
public int[] getRandomArray() {
List<WordsStock> wordsStocks = globalStock;
Collections.shuffle(wordsStocks);
int[] array = new int[getGlobalStockCount()];
for(int i = 0; i < array.length; i++) {
array[i] = wordsStocks.get(i).getCharactersInWord();
}
return array;
}The first method is very simple and used to get the amount of groups. I can return static number (4), but I return the size of the List, thus It’s needed for future scalability of the algorithm. For example, if you will use another Vocabulary, other approaches, or else.
Get Dmytro B’s stories in your inbox
Join Medium for free to get updates from this writer.
The second method returns an array for generation. This method returns arrays in the next formats:
[4, 1, 3, 2]
[1, 4, 2, 3]
[3, 2, 4, 1]
...Its array defines the words' index order. For example, if we have the next array:
[4, 1, 3, 2]First, we tried to find a word in the group [1000;9999], after [0;9], and so on. Since we’re generating a phrase of 12 words, we need 3 arrays that equal 12. After getting 3 arrays it needs to be joined in one array. As you can see in the code above, I get the default ordering of WordsStock and use shuffle to randomize an order in the array.
In the text above I said about random data that is used to generate mnemonic. Using the standard utility Random() is not correct, thus we need to use something more unique for this goal. I think timestamps can be a good solution for this, but the timestamp is not a final random string, because it can be unsafe for generate. We need to do something with that value. Multiplying on static of random value can give us a wrong result (partly randomly) or a very big value. I propose to multiply the timestamp of the random coefficient from Math.random(). But not once. Look at the picture below.
On diagrams you see not one timestamp, I defined two different timestamps. Why? According to server properties, time on iterations can be different at the same time. And because of that I defined a second timestamp that initializes after fully completed loop iterations on the first timestamp. It provides more secure methods and has an unreal chance to guess or match combinations according to time. Iterations can provide more random results, instead of a single multiplying process. At the end, when we have two modified timestamps in the correct order, we need to cast all timestamps to Long, reverse the first row (first timestamp), and append the second value (end timestamp), using StringBuilder. That’s all! Now we have some algorithm to generate random strings.
public class StringService {
private final int RANDOM_STRING_ITERATION = 50;
public String getRandomString() {
long startTimestamp = System.currentTimeMillis();
long startRandomLong = startTimestamp;
for(int i = 0; i < RANDOM_STRING_ITERATION; i++) {
double randomFactor = Math.random();
startRandomLong = (long) (startTimestamp * randomFactor);
}
long endTimestamp = System.currentTimeMillis();
long endRandomLong = endTimestamp;
for(int i = 0; i < RANDOM_STRING_ITERATION; i++) {
double randomFactor = Math.random();
endRandomLong = (long) (endTimestamp * randomFactor);
}
return new StringBuilder()
.append(startRandomLong)
.reverse()
.append(endRandomLong)
.toString();
}
}Looks not difficult, but very interesting. This method returns strings at the next formats:
81487368419111341191864738
40049074002582671928167
600102677063553596666738
...This method will called not once because this random string has a chance to not consist of needed indexes for the 4-th group of word indexes. For example, index 7839 is not valid, because we have a range [0;2047]. So, during the generate process we can call this method and replace the string to get the needed index. And yes, we need to replace string and use it for whole words, but not for one. I will provide more information about the algorithm below.
And now, when we have all the necessary services and information, we can start to write the algorithm. Go to our Starter class and add the first method:
private static List<Integer> getMnemonicArray() {
List<Integer> mnemonicArray = new ArrayList<>();
for (int i = 0; i < WordsStockService.LIMIT; i++) {
int[] array = wordsStockService.getRandomArray();
for (int k : array) {
mnemonicArray.add(k);
}
}
return mnemonicArray;
}Using this method we create a List with shuffled indexes. This List contains an order of words in our mnemonic. But it is not a words index, it’s only the amount of characters in the index. This method returns the following results:
[4, 3, 1, 2, 4, 2, 1, 3, 1, 2, 3, 4]
[2, 3, 1, 4, 3, 2, 1, 4, 4, 2, 1, 3]
[2, 4, 1, 3, 4, 1, 3, 2, 2, 3, 1, 4]
...As you can see, it’s only an order of indexes. After generation of the final mnemonic array, our last step is mnemonic generation.
StringBuilder finalStringBuilder = new StringBuilder(initRandomString);
while (!mnemonicArray.isEmpty()) {
int characterSize = mnemonicArray.get(0);
if (finalStringBuilder.length() < characterSize) {
finalStringBuilder.append(stringService.getRandomString());
}
int wordIndex = Integer.parseInt(finalStringBuilder.substring(0, characterSize));
String word = vocabularyService.getWordByIndex(wordIndex);
if (word != null) {
mnemonicService.addWordToMnemonic(word);
mnemonicArray.remove(0);
}
finalStringBuilder.delete(0, characterSize);
}We pass our generated String to StringBuilder and start the loop while our mnemonic array with indexes contains the indexes. Each iteration we cut the random string from the start to word index. For example, if the word index equals 2, we will cut String from 0 to 2. When we came up to the 4-th group, we tried to get words from Vocabulary, but if an index was not in the necessary diapason [0;2047] we just cut the string and removed chosen characters. For each iteration, we check if our random string is less than needed, we generate a new random string and append it to StringBuilder. It looks like replacing a random String, but we don’t change the order of a mnemonic array. If a word exists, we remove the index from the mnemonic array and add a word to the mnemonic. Nothing else.
And run that:
public static void main(String[] args) {
String mnemonic = generateMnemonic();
System.out.println(mnemonic);
}The outputs will be:
[tray, gauge, bamboo, actual, actress, total, journey, absurd, essence, about, abstract, arm]
[army, broken, across, abuse, excuse, impose, wolf, arrange, spice, about, absent, arch]
[elbow, fatigue, absurd, album, dizzy, portion, above, blanket, absorb, anger, attitude]
....Lastly, It’s not the ideal realization, it’s just the example of one approach to realize own algorithm. You can easily modify that code to get more secure solutions.
In the next articles, I want to show you how you can realize games or something like that using Blockchain approaches.
The source code of this project is available on the GitHub Repository.
Happy hacking!









