HD Keychain.
Now that I’ve created my keychain object I just need to write a command line interface for the user to interact with the class. To do this I’ll need to start with a separate file and in that file I’m going to use my usual includes and namespace but with the addition of the file that contains the HD wallet class I’ve just created.
#include <bitcoin/bitcoin.hpp> #include <string.h> #include <iostream> #include "HD_Wallet.cpp" using namespace bc;
Now, for this program I’m going to want a main menu and a sub-menu for the display options.
void mainMenu() { std::cout << "\n==========================" << std::endl; std::cout << "1. Generate New Wallet" << std::endl; std::cout << "2. Import Wallet" << std::endl; std::cout << "3. display" << std::endl; std::cout << "4. Exit" << std::endl; std::cout << "==========================" << std::endl; } void displayMenu() { std::cout << "\n==========================" << std::endl; std::cout << "1. Private Key" << std::endl; std::cout << "2. Mnemonic" << std::endl; std::cout << "3. Child Private Key" << std::endl; std::cout << "4. Payment Address" << std::endl; std::cout << "5. Address Range" << std::endl; std::cout << "6. Dump Keys" << std::endl; std::cout << "7. Exit" << std::endl; std::cout << "==========================" << std::endl; }
Once I have my menu I’m going to want to write a function to get the user’s menu selection and return it as an integer. Simple enough with the standard library.
int getInput() { int choice; std::cout << "Enter a Number: "; std::cin >> choice; return choice; }
I also want the user to be able to import a wallet via it’s seed, so I’ll need a function that takes the users mnemonic and converts it to a word_list object before returning it. Note that the split function here splits a string up by each space and puts it into a word list.
wallet::word_list getWordList() { std::string mnemonicSeed; std::cout << "Enter Seed String:"; cin.ignore(); std::getline(cin, mnemonicSeed); return split(mnemonicSeed); }
Okay, with these helper functions done, I now need a function to initialize my HD_Wallet object from 128-bits of entropy and then return that object. For good measure I’m also going to have this function call it’s dumpKeys member and display all the newly created keys to the user.
HD_Wallet randomGen() { data_chunk entropyChunk = data_chunk(16); pseudo_random_fill(entropyChunk); HD_Wallet(entropyChunk).dumpKeys(); return HD_Wallet(entropyChunk); }
With that done, I’m now going to write my driver function. Everything from here on is going to go in the main function.
Starting with initializing a wallet object using the randomGen() function.
int main(){ HD_Wallet wallet = randomGen(); }
For the user interface, I’m going to use a while loop and since “option 4” maps to exit in the menu I’m going to have it loop as long as choice does not equal 4. To get this loop started I’m going set the choice variable to 0 and immediately inside the while loop call the main menu function and setting the choice variable to whatever gets return by the get input function.
int choice = 0; while(choice != 4) { mainMenu(); choice = getInput(); }
From there, I just fill out if statements with functions corresponding to the options in the menu.
Here I’ve filled in the options for the two types of wallet constructors.
int choice = 0; while(choice != 4) { mainMenu(); choice = getInput(); if(choice == 1) { wallet = randomGen(); } else if(choice == 2) { wallet::word_list mnemonicSeedList = getWordList(); if(wallet::validate_mnemonic(mnemonicSeedList)) { wallet = HD_Wallet(mnemonicSeedList); wallet.dumpKeys(); } else { std::cout << "Mnemonic Invalid!" << std::endl; } } }
Now, for the display menu I just need to replicate this while loop and simply nest it inside another if statement but instead of calling the main menu I need to call the display menu. Similarly, I need to fill out the if statements inside of this while loop with the corresponding options in the display menu.
int choice = 0; while(choice != 4) { mainMenu(); choice = getInput(); if(choice == 1) { wallet = randomGen(); } else if(choice == 2) { wallet::word_list mnemonicSeedList = getWordList(); if(wallet::validate_mnemonic(mnemonicSeedList)) { wallet = HD_Wallet(mnemonicSeedList); wallet.dumpKeys(); } else { std::cout << "Mnemonic Invalid!" << std::endl; } } }else if (choice == 3) { while(choice != 7){ displayMenu(); choice = getInput(); std::cin.ignore(); int index; if(choice == 1) { wallet.displayPrivateKey(); } else if(choice == 2) { wallet.displayMnemonic(); } else if (choice == 3) { index = getInput(); wallet.displayChildPrivateKey(index); } else if (choice == 4) { index = getInput(); wallet.displayAddress(index); } else if (choice == 5) { int start = getInput(); int end = getInput(); wallet.addressRange(start, end); }else if (choice == 6) { wallet.dumpKeys(); }else if (choice == 7) { std::cout << "Bye" << std::endl; } else { std::cout << "Selection Invalid! " << std::endl; } } } }
Now I just need to add a goodbye message and an invalid input catcher to the main while loop and the program is ready to go. The complete main function should look like this:
int main(){ HD_Wallet wallet = randomGen(); int choice = 0; while(choice != 4) { mainMenu(); choice = getInput(); if(choice == 1) { wallet = randomGen(); } else if(choice == 2) { wallet::word_list mnemonicSeedList = getWordList(); if(wallet::validate_mnemonic(mnemonicSeedList)) { wallet = HD_Wallet(mnemonicSeedList); wallet.dumpKeys(); } else { std::cout << "Mnemonic Invalid!" << std::endl; } } else if (choice == 3) { while(choice != 7){ displayMenu(); choice = getInput(); std::cin.ignore(); int index; if(choice == 1) { wallet.displayPrivateKey(); } else if(choice == 2) { wallet.displayMnemonic(); } else if (choice == 3) { index = getInput(); wallet.displayChildPrivateKey(index); } else if (choice == 4) { index = getInput(); wallet.displayAddress(index); } else if (choice == 5) { int start = getInput(); int end = getInput(); wallet.addressRange(start, end); }else if (choice == 6) { wallet.dumpKeys(); }else if (choice == 7) { std::cout << "Bye" << std::endl; } else { std::cout << "Selection Invalid! " << std::endl; } } } else if (choice == 4) { std::cout << "Goodbye" << std::endl; }else { std::cout << "Selection Invalid!" << std::endl; } } }
The entire program can be found at https://github.com/AaronJaramillo/LibbitcoinTutorial
The program can now be compile as usual, though this time passing both files to the compiler, with:
$ g++ -std=c++11 -o HDwallet HD_wallet.cpp wallet.cpp $(pkg-config --cflags libbitcoin --libs libbitcoin) $ ./HDwallet
And the initial output should look something like this: