Airgapped hardware wallet based on STM32F469-Discovery board using SD card to pass transaction data

Dependencies:   mbed QSPI_DISCO_F469NI BSP_DISCO_F469NI

Committer:
stepansnigirev
Date:
Tue Jul 30 19:25:52 2019 +0000
Revision:
4:73e20d662d73
Parent:
2:120e0ca2f3a7
comments

Who changed what in which revision?

UserRevisionLine numberNew contents of line
stepansnigirev 0:f43431023689 1 #include "mbed.h"
stepansnigirev 0:f43431023689 2 #include "helpers.h"
stepansnigirev 0:f43431023689 3
stepansnigirev 0:f43431023689 4 // bitcoin lib
stepansnigirev 4:73e20d662d73 5 #include "Bitcoin.h" // Public, Private, HD keys, scripts, raw transactions and stuff
stepansnigirev 4:73e20d662d73 6 #include "PSBT.h" // Partially Signed Bitcoin Transaction format
stepansnigirev 0:f43431023689 7
stepansnigirev 4:73e20d662d73 8 /***************** bitcoin stuff ***************/
stepansnigirev 0:f43431023689 9
stepansnigirev 1:b88ef7630eb3 10 string mnemonic;
stepansnigirev 0:f43431023689 11 HDPrivateKey root; // root private key
stepansnigirev 0:f43431023689 12 HDPrivateKey account; // account master private key
stepansnigirev 0:f43431023689 13 HDPublicKey xpub; // account master public key
stepansnigirev 0:f43431023689 14 bool change = false; // internal or external address
stepansnigirev 0:f43431023689 15 unsigned int child_index = 0; // current child index
stepansnigirev 0:f43431023689 16
stepansnigirev 0:f43431023689 17 PSBT psbt; // psbt transaction we will be signing
stepansnigirev 0:f43431023689 18
stepansnigirev 4:73e20d662d73 19 /****************** GUI elements ****************/
stepansnigirev 4:73e20d662d73 20
stepansnigirev 4:73e20d662d73 21 // some global scope GUI objects we will be changing in callbacks
stepansnigirev 4:73e20d662d73 22 Label titleLbl;
stepansnigirev 4:73e20d662d73 23 Label dataLbl;
stepansnigirev 4:73e20d662d73 24 QR qr;
stepansnigirev 4:73e20d662d73 25
stepansnigirev 4:73e20d662d73 26 Button btn;
stepansnigirev 4:73e20d662d73 27 Button printBtn;
stepansnigirev 4:73e20d662d73 28 Label lbl;
stepansnigirev 4:73e20d662d73 29
stepansnigirev 4:73e20d662d73 30 /*********** forward declarations **************/
stepansnigirev 4:73e20d662d73 31
stepansnigirev 4:73e20d662d73 32 // handy function to display information to the user
stepansnigirev 4:73e20d662d73 33 // with a title, a message and OK button that goes to the menu
stepansnigirev 4:73e20d662d73 34 void showMessage(const string title, const string message);
stepansnigirev 4:73e20d662d73 35 // signs transaction after user confirmation
stepansnigirev 4:73e20d662d73 36 static lv_res_t signConfirmCallback(lv_obj_t * btn);
stepansnigirev 4:73e20d662d73 37 // derives keys from mnemonic and password
stepansnigirev 4:73e20d662d73 38 void initKeys(const string mnemonic, const string password = "");
stepansnigirev 4:73e20d662d73 39 // if mnemonic is not present we can generate or recover it
stepansnigirev 4:73e20d662d73 40 void showInitScreen();
stepansnigirev 4:73e20d662d73 41 // if mnemonic is there we go directly to the main menu
stepansnigirev 4:73e20d662d73 42 void showMenu();
stepansnigirev 4:73e20d662d73 43 // some functions that handle button clicks
stepansnigirev 4:73e20d662d73 44 static lv_res_t toMenuCallback(lv_obj_t * btn);
stepansnigirev 4:73e20d662d73 45 static lv_res_t showAddressesCallback(lv_obj_t * btn);
stepansnigirev 4:73e20d662d73 46 static lv_res_t showMnemonicCallback(lv_obj_t * btn);
stepansnigirev 4:73e20d662d73 47 static lv_res_t wipeCallback(lv_obj_t * btn);
stepansnigirev 4:73e20d662d73 48 static lv_res_t enterMnemonicCallback(lv_obj_t * btn);
stepansnigirev 4:73e20d662d73 49 // shows address on the screen
stepansnigirev 4:73e20d662d73 50 void showAddress(unsigned int child_index, bool change);
stepansnigirev 4:73e20d662d73 51
stepansnigirev 4:73e20d662d73 52 /*********** functions to complete **************/
stepansnigirev 4:73e20d662d73 53
stepansnigirev 4:73e20d662d73 54 // generates a new mnemonic
stepansnigirev 4:73e20d662d73 55 string generateNewMnemonic(){
stepansnigirev 4:73e20d662d73 56 // TODO:
stepansnigirev 4:73e20d662d73 57 // - generate random buffer (16 or 32 bytes) - getRandomBuffer(buf, size)
stepansnigirev 4:73e20d662d73 58 // - create a new mnemonic from it - generateMnemonic(buf, size)
stepansnigirev 4:73e20d662d73 59 // - save this mnemonic
stepansnigirev 4:73e20d662d73 60 // - display it to the user
stepansnigirev 4:73e20d662d73 61
stepansnigirev 4:73e20d662d73 62 uint8_t randomBuffer[16];
stepansnigirev 4:73e20d662d73 63 getRandomBuffer(randomBuffer, sizeof(randomBuffer));
stepansnigirev 4:73e20d662d73 64 string mnemonic = generateMnemonic(randomBuffer, sizeof(randomBuffer));
stepansnigirev 4:73e20d662d73 65 return mnemonic;
stepansnigirev 4:73e20d662d73 66 }
stepansnigirev 4:73e20d662d73 67
stepansnigirev 4:73e20d662d73 68 // checks if entered mnemonic is valid
stepansnigirev 4:73e20d662d73 69 static lv_res_t checkMnemonicCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 70 // TODO:
stepansnigirev 4:73e20d662d73 71 // - check mnemonic
stepansnigirev 4:73e20d662d73 72 // - if ok, init keys and show success message
stepansnigirev 4:73e20d662d73 73 // - otherwise erase the whole mnemonic
stepansnigirev 4:73e20d662d73 74
stepansnigirev 4:73e20d662d73 75 if(checkMnemonic(mnemonic.c_str())){
stepansnigirev 4:73e20d662d73 76 saveMnemonic(mnemonic);
stepansnigirev 4:73e20d662d73 77 initKeys(mnemonic);
stepansnigirev 4:73e20d662d73 78 showMessage("Mnemonic is ok", "Recovered sucessfully");
stepansnigirev 4:73e20d662d73 79 }else{
stepansnigirev 4:73e20d662d73 80 mnemonic = "";
stepansnigirev 4:73e20d662d73 81 dataLbl.text(mnemonic);
stepansnigirev 4:73e20d662d73 82 }
stepansnigirev 4:73e20d662d73 83 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 84 }
stepansnigirev 4:73e20d662d73 85
stepansnigirev 4:73e20d662d73 86 // generates hd keys from the mnemonic
stepansnigirev 4:73e20d662d73 87 void initKeys(const string mnemonic, const string password){
stepansnigirev 4:73e20d662d73 88 // TODO:
stepansnigirev 4:73e20d662d73 89 // - derive root key from the mnemonic and empty password
stepansnigirev 4:73e20d662d73 90 // - derive account key using m/84'/1'/0'/ derivation path
stepansnigirev 4:73e20d662d73 91 // - get account master public key
stepansnigirev 4:73e20d662d73 92
stepansnigirev 4:73e20d662d73 93 root.fromMnemonic(mnemonic, password);
stepansnigirev 4:73e20d662d73 94 account = root.derive("m/84'/1'/0'/");
stepansnigirev 4:73e20d662d73 95 xpub = account.xpub();
stepansnigirev 4:73e20d662d73 96 }
stepansnigirev 4:73e20d662d73 97
stepansnigirev 4:73e20d662d73 98 void showAddress(unsigned int child_index, bool change){
stepansnigirev 4:73e20d662d73 99 // TODO:
stepansnigirev 4:73e20d662d73 100 // - derive an address from xpub according to the derivation
stepansnigirev 4:73e20d662d73 101 // - set dataLbl text to the address
stepansnigirev 4:73e20d662d73 102 // - set qr text to "bitcoin:address"
stepansnigirev 4:73e20d662d73 103 // OPTIONAL:
stepansnigirev 4:73e20d662d73 104 // - display both bech32 and nested segwit addresses
stepansnigirev 4:73e20d662d73 105
stepansnigirev 4:73e20d662d73 106 stringstream title;
stepansnigirev 4:73e20d662d73 107 title << "Your ";
stepansnigirev 4:73e20d662d73 108 if(change){
stepansnigirev 4:73e20d662d73 109 title << "change ";
stepansnigirev 4:73e20d662d73 110 }else{
stepansnigirev 4:73e20d662d73 111 title << "receiving ";
stepansnigirev 4:73e20d662d73 112 }
stepansnigirev 4:73e20d662d73 113 title << "address #" << child_index << ":";
stepansnigirev 4:73e20d662d73 114 titleLbl.text(title.str());
stepansnigirev 4:73e20d662d73 115 // generate the address here
stepansnigirev 4:73e20d662d73 116 string address = xpub.child(change).child(child_index).address();
stepansnigirev 4:73e20d662d73 117 dataLbl.text(address);
stepansnigirev 4:73e20d662d73 118 dataLbl.align(ALIGN_CENTER);
stepansnigirev 4:73e20d662d73 119 qr.text(string("bitcoin:") + address);
stepansnigirev 4:73e20d662d73 120 qr.align(ALIGN_CENTER);
stepansnigirev 4:73e20d662d73 121 }
stepansnigirev 4:73e20d662d73 122
stepansnigirev 4:73e20d662d73 123 // this will be called when we press "save master key" button
stepansnigirev 4:73e20d662d73 124 static lv_res_t saveXpubCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 125 // TODO:
stepansnigirev 4:73e20d662d73 126 // - check if SD card present
stepansnigirev 4:73e20d662d73 127 // - save xpub to the "xpub.txt"
stepansnigirev 4:73e20d662d73 128 // - check if write was sucessful
stepansnigirev 4:73e20d662d73 129 // - show success / fail message
stepansnigirev 4:73e20d662d73 130 // OPTIONAL:
stepansnigirev 4:73e20d662d73 131 // - use [fingerprint/derivation]xpub format
stepansnigirev 4:73e20d662d73 132 // - create bitcoin core descriptor for bitcoin-cli importmulti
stepansnigirev 4:73e20d662d73 133
stepansnigirev 4:73e20d662d73 134 if(!SD.detected()){
stepansnigirev 4:73e20d662d73 135 showMessage("Error", "SD card is not present");
stepansnigirev 4:73e20d662d73 136 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 137 }
stepansnigirev 4:73e20d662d73 138 int res = SD.save("xpub.txt", xpub.toString());
stepansnigirev 4:73e20d662d73 139 if(res != SD_SUCCESS){
stepansnigirev 4:73e20d662d73 140 showMessage("Error", "Something wrong with SD card");
stepansnigirev 4:73e20d662d73 141 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 142 }
stepansnigirev 4:73e20d662d73 143 showMessage("Success","Master public key is saved to the SD card");
stepansnigirev 4:73e20d662d73 144 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 145 }
stepansnigirev 4:73e20d662d73 146
stepansnigirev 4:73e20d662d73 147 // displays confirmation screen for the transaction signing
stepansnigirev 4:73e20d662d73 148 void showSignRequest(){
stepansnigirev 4:73e20d662d73 149 // TODO:
stepansnigirev 4:73e20d662d73 150 // display information of the transaction:
stepansnigirev 4:73e20d662d73 151 // - go through all outputs
stepansnigirev 4:73e20d662d73 152 // - detect if the address is the change address
stepansnigirev 4:73e20d662d73 153 // - if not, show information in the form "address: amount"
stepansnigirev 4:73e20d662d73 154 // - if it is change, hide or mark as a change
stepansnigirev 4:73e20d662d73 155 // - display the transaction fee
stepansnigirev 4:73e20d662d73 156 // OPTIONAL:
stepansnigirev 4:73e20d662d73 157 // - verify that pubkey is actually used in the script
stepansnigirev 4:73e20d662d73 158 // - do the same for bip49 and check redeem script
stepansnigirev 4:73e20d662d73 159 // - check if derivation path is not weird (indexes are reasonable)
stepansnigirev 4:73e20d662d73 160
stepansnigirev 4:73e20d662d73 161 stringstream ss;
stepansnigirev 4:73e20d662d73 162 ss << "Sending:\n\n";
stepansnigirev 4:73e20d662d73 163 for(unsigned int i=0; i<psbt.tx.outputsNumber; i++){
stepansnigirev 4:73e20d662d73 164 bool mine = psbt.isMine(i, account.xpub());
stepansnigirev 4:73e20d662d73 165 ss << psbt.tx.txOuts[i].address(&Testnet);
stepansnigirev 4:73e20d662d73 166 if(mine){
stepansnigirev 4:73e20d662d73 167 ss << " (change)";
stepansnigirev 4:73e20d662d73 168 }
stepansnigirev 4:73e20d662d73 169 ss << ": " << (float(psbt.tx.txOuts[i].amount)/1e5) << " mBTC\n\n";
stepansnigirev 4:73e20d662d73 170 }
stepansnigirev 4:73e20d662d73 171 ss << "Fee: " << (float(psbt.fee())/1e5) << " mBTC";
stepansnigirev 4:73e20d662d73 172
stepansnigirev 4:73e20d662d73 173 gui.clear();
stepansnigirev 4:73e20d662d73 174 titleLbl = Label("Sign transaction?");
stepansnigirev 4:73e20d662d73 175 titleLbl.size(gui.width(), 20);
stepansnigirev 4:73e20d662d73 176 titleLbl.position(0, 40);
stepansnigirev 4:73e20d662d73 177 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 4:73e20d662d73 178
stepansnigirev 4:73e20d662d73 179 dataLbl = Label(ss.str());
stepansnigirev 4:73e20d662d73 180 dataLbl.size(gui.width()-100, 100);
stepansnigirev 4:73e20d662d73 181 dataLbl.position(50, 300);
stepansnigirev 4:73e20d662d73 182 dataLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 4:73e20d662d73 183
stepansnigirev 4:73e20d662d73 184 Button btn(toMenuCallback, "Cancel");
stepansnigirev 4:73e20d662d73 185 btn.size(gui.width()/2-45, 80);
stepansnigirev 4:73e20d662d73 186 btn.position(30, gui.height()-100);
stepansnigirev 4:73e20d662d73 187
stepansnigirev 4:73e20d662d73 188 Button btn2(signConfirmCallback, "Confirm");
stepansnigirev 4:73e20d662d73 189 btn2.size(gui.width()/2-45, 80);
stepansnigirev 4:73e20d662d73 190 btn2.position(gui.width()/2+30, gui.height()-100);
stepansnigirev 4:73e20d662d73 191 }
stepansnigirev 4:73e20d662d73 192
stepansnigirev 4:73e20d662d73 193 // reads unsigned transaction from SD card
stepansnigirev 4:73e20d662d73 194 static lv_res_t signPSBTCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 195 // TODO:
stepansnigirev 4:73e20d662d73 196 // - check if SD card is there
stepansnigirev 4:73e20d662d73 197 // - read data from "unsigned.psbt"
stepansnigirev 4:73e20d662d73 198 // - convert it from base64 to raw bytes
stepansnigirev 4:73e20d662d73 199 // - parse psbt transaction
stepansnigirev 4:73e20d662d73 200 // - call showSignRequest()
stepansnigirev 4:73e20d662d73 201 // OPTIONAL
stepansnigirev 4:73e20d662d73 202 // - also show signed transaction as a QR code
stepansnigirev 4:73e20d662d73 203
stepansnigirev 4:73e20d662d73 204 if(!SD.detected()){
stepansnigirev 4:73e20d662d73 205 showMessage("Error", "SD card is not present");
stepansnigirev 4:73e20d662d73 206 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 207 }
stepansnigirev 4:73e20d662d73 208 string s = SD.read("unsigned.psbt");
stepansnigirev 4:73e20d662d73 209 if(s.length() == 0){
stepansnigirev 4:73e20d662d73 210 showMessage("Fail", "Can't read unsigned.psbt file");
stepansnigirev 4:73e20d662d73 211 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 212 }
stepansnigirev 4:73e20d662d73 213 uint8_t * raw = new uint8_t[s.length()];
stepansnigirev 4:73e20d662d73 214 unsigned int len = fromBase64(s.c_str(), s.length(), raw, s.length());
stepansnigirev 4:73e20d662d73 215 if(len == 0){
stepansnigirev 4:73e20d662d73 216 delete [] raw;
stepansnigirev 4:73e20d662d73 217 showMessage("Fail", "Can't convert from base64");
stepansnigirev 4:73e20d662d73 218 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 219 }
stepansnigirev 4:73e20d662d73 220 psbt.reset();
stepansnigirev 4:73e20d662d73 221 len = psbt.parse(raw, len);
stepansnigirev 4:73e20d662d73 222 delete [] raw;
stepansnigirev 4:73e20d662d73 223 if(len == 0){
stepansnigirev 4:73e20d662d73 224 showMessage("Fail", "Can't parse PSBT");
stepansnigirev 4:73e20d662d73 225 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 226 }
stepansnigirev 4:73e20d662d73 227 showSignRequest();
stepansnigirev 4:73e20d662d73 228 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 229 }
stepansnigirev 4:73e20d662d73 230
stepansnigirev 4:73e20d662d73 231 // signs transaction
stepansnigirev 4:73e20d662d73 232 static lv_res_t signConfirmCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 233 // TODO:
stepansnigirev 4:73e20d662d73 234 // - check if SD card is still there
stepansnigirev 4:73e20d662d73 235 // - serialize the psbt to byte array
stepansnigirev 4:73e20d662d73 236 // - convert to base64
stepansnigirev 4:73e20d662d73 237 // - save to "signed.psbt"
stepansnigirev 4:73e20d662d73 238 // - show success message
stepansnigirev 4:73e20d662d73 239
stepansnigirev 4:73e20d662d73 240 if(!SD.detected()){
stepansnigirev 4:73e20d662d73 241 showMessage("Error", "SD card is not present");
stepansnigirev 4:73e20d662d73 242 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 243 }
stepansnigirev 4:73e20d662d73 244 psbt.sign(account);
stepansnigirev 4:73e20d662d73 245
stepansnigirev 4:73e20d662d73 246 uint8_t * raw = (uint8_t *)calloc(psbt.length(), sizeof(uint8_t));
stepansnigirev 4:73e20d662d73 247 size_t len = psbt.serialize(raw, psbt.length());
stepansnigirev 4:73e20d662d73 248 psbt = PSBT();
stepansnigirev 4:73e20d662d73 249 string b64 = toBase64(raw, len);
stepansnigirev 4:73e20d662d73 250
stepansnigirev 4:73e20d662d73 251 int res = SD.save("signed.psbt", b64.c_str());
stepansnigirev 4:73e20d662d73 252 if(res != SD_SUCCESS){
stepansnigirev 4:73e20d662d73 253 showMessage("Error", "Something wrong with SD card");
stepansnigirev 4:73e20d662d73 254 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 255 }
stepansnigirev 4:73e20d662d73 256 showMessage("Success", "Signed transaction saved to the SD card");
stepansnigirev 4:73e20d662d73 257 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 258 }
stepansnigirev 4:73e20d662d73 259
stepansnigirev 4:73e20d662d73 260 /***************** GUI functions ***************/
stepansnigirev 4:73e20d662d73 261
stepansnigirev 4:73e20d662d73 262 static lv_res_t newMnemonicCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 263 string mnemonic = generateNewMnemonic();
stepansnigirev 4:73e20d662d73 264 saveMnemonic(mnemonic);
stepansnigirev 4:73e20d662d73 265 showMessage("Write down your recovery phrase:", mnemonic);
stepansnigirev 4:73e20d662d73 266 initKeys(mnemonic);
stepansnigirev 4:73e20d662d73 267 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 268 }
stepansnigirev 4:73e20d662d73 269
stepansnigirev 4:73e20d662d73 270 // show next address
stepansnigirev 4:73e20d662d73 271 static lv_res_t nextCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 272 child_index++;
stepansnigirev 4:73e20d662d73 273 showAddress(child_index, change);
stepansnigirev 4:73e20d662d73 274 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 275 }
stepansnigirev 4:73e20d662d73 276
stepansnigirev 4:73e20d662d73 277 // show previous address
stepansnigirev 4:73e20d662d73 278 static lv_res_t prevCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 279 if(child_index > 0){
stepansnigirev 4:73e20d662d73 280 child_index--;
stepansnigirev 4:73e20d662d73 281 showAddress(child_index, change);
stepansnigirev 4:73e20d662d73 282 }
stepansnigirev 4:73e20d662d73 283 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 284 }
stepansnigirev 4:73e20d662d73 285
stepansnigirev 4:73e20d662d73 286 // switch to change addresses
stepansnigirev 4:73e20d662d73 287 static lv_res_t changeCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 288 change = !change;
stepansnigirev 4:73e20d662d73 289 showAddress(child_index, change);
stepansnigirev 4:73e20d662d73 290 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 291 }
stepansnigirev 4:73e20d662d73 292
stepansnigirev 4:73e20d662d73 293 // show master public key
stepansnigirev 4:73e20d662d73 294 static lv_res_t xpubCallback(lv_obj_t * btn){
stepansnigirev 4:73e20d662d73 295 titleLbl.text("Your master public key");
stepansnigirev 4:73e20d662d73 296 qr.text(xpub.toString());
stepansnigirev 4:73e20d662d73 297 dataLbl.text(xpub.toString());
stepansnigirev 4:73e20d662d73 298 qr.align(ALIGN_CENTER);
stepansnigirev 4:73e20d662d73 299 dataLbl.align(ALIGN_CENTER);
stepansnigirev 4:73e20d662d73 300 return LV_RES_OK;
stepansnigirev 4:73e20d662d73 301 }
stepansnigirev 4:73e20d662d73 302
stepansnigirev 0:f43431023689 303 /******************* Main part *****************/
stepansnigirev 0:f43431023689 304
stepansnigirev 0:f43431023689 305 int main(){
stepansnigirev 0:f43431023689 306 init();
stepansnigirev 0:f43431023689 307
stepansnigirev 0:f43431023689 308 string mnemonic = loadMnemonic();
stepansnigirev 0:f43431023689 309 if(mnemonic.length() == 0){
stepansnigirev 0:f43431023689 310 showInitScreen();
stepansnigirev 0:f43431023689 311 }else{
stepansnigirev 0:f43431023689 312 initKeys(mnemonic);
stepansnigirev 0:f43431023689 313 showMenu();
stepansnigirev 0:f43431023689 314 }
stepansnigirev 0:f43431023689 315
stepansnigirev 0:f43431023689 316 while(1){
stepansnigirev 0:f43431023689 317 gui.update();
stepansnigirev 0:f43431023689 318 }
stepansnigirev 0:f43431023689 319 }
stepansnigirev 0:f43431023689 320
stepansnigirev 0:f43431023689 321 /****************** GUI stuff *****************/
stepansnigirev 4:73e20d662d73 322
stepansnigirev 0:f43431023689 323 void showInitScreen(){
stepansnigirev 0:f43431023689 324 gui.clear();
stepansnigirev 0:f43431023689 325 titleLbl = Label("Let's set it up!");
stepansnigirev 0:f43431023689 326 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 327 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 328 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 329
stepansnigirev 0:f43431023689 330 Button btn(newMnemonicCallback, "Generate new mnemonic");
stepansnigirev 0:f43431023689 331 btn.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 332 btn.position(0, 200);
stepansnigirev 0:f43431023689 333 btn.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 334
stepansnigirev 0:f43431023689 335 Button btn2(enterMnemonicCallback, "Enter existing mnemonic");
stepansnigirev 0:f43431023689 336 btn2.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 337 btn2.position(0, 300);
stepansnigirev 0:f43431023689 338 btn2.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 339 }
stepansnigirev 0:f43431023689 340
stepansnigirev 0:f43431023689 341 void showMenu(){
stepansnigirev 0:f43431023689 342 gui.clear();
stepansnigirev 0:f43431023689 343 titleLbl = Label("What do you want to do?");
stepansnigirev 0:f43431023689 344 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 345 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 346 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 347
stepansnigirev 0:f43431023689 348 Button btn(showAddressesCallback, "Show addresses");
stepansnigirev 0:f43431023689 349 btn.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 350 btn.position(0, 100);
stepansnigirev 0:f43431023689 351 btn.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 352
stepansnigirev 0:f43431023689 353 Button btn2(saveXpubCallback, "Export xpub");
stepansnigirev 0:f43431023689 354 btn2.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 355 btn2.position(0, 300);
stepansnigirev 0:f43431023689 356 btn2.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 357
stepansnigirev 0:f43431023689 358 Button btn3(signPSBTCallback, "Sign PSBT transaction");
stepansnigirev 0:f43431023689 359 btn3.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 360 btn3.position(0, 400);
stepansnigirev 0:f43431023689 361 btn3.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 362
stepansnigirev 0:f43431023689 363 Button btn4(wipeCallback, "Wipe device");
stepansnigirev 0:f43431023689 364 btn4.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 365 btn4.position(0, 600);
stepansnigirev 0:f43431023689 366 btn4.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 367
stepansnigirev 0:f43431023689 368 Button btn5(showMnemonicCallback, "Show mnemonic");
stepansnigirev 0:f43431023689 369 btn5.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 370 btn5.position(0, 700);
stepansnigirev 0:f43431023689 371 btn5.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 372 }
stepansnigirev 0:f43431023689 373
stepansnigirev 0:f43431023689 374 void showMessage(const string title, const string message){
stepansnigirev 0:f43431023689 375 gui.clear();
stepansnigirev 0:f43431023689 376 titleLbl = Label(title);
stepansnigirev 0:f43431023689 377 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 378 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 379 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 380
stepansnigirev 0:f43431023689 381 dataLbl = Label(message);
stepansnigirev 0:f43431023689 382 dataLbl.size(gui.width()-100, 100);
stepansnigirev 0:f43431023689 383 dataLbl.position(50, 300);
stepansnigirev 0:f43431023689 384 dataLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 385
stepansnigirev 0:f43431023689 386 Button btn(toMenuCallback, "OK");
stepansnigirev 0:f43431023689 387 btn.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 388 btn.position(0, gui.height()-100);
stepansnigirev 0:f43431023689 389 btn.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 390 }
stepansnigirev 0:f43431023689 391
stepansnigirev 0:f43431023689 392 void showAddressScreen(){
stepansnigirev 0:f43431023689 393 gui.clear();
stepansnigirev 0:f43431023689 394 titleLbl = Label("Your address");
stepansnigirev 0:f43431023689 395 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 396 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 397 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 398
stepansnigirev 0:f43431023689 399 dataLbl = Label(" ");
stepansnigirev 0:f43431023689 400 dataLbl.size(gui.width()-100, 100); // full width
stepansnigirev 0:f43431023689 401 dataLbl.position(50, gui.height()-300);
stepansnigirev 0:f43431023689 402 dataLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 403
stepansnigirev 0:f43431023689 404 qr = QR(" ");
stepansnigirev 0:f43431023689 405 qr.size(gui.width()-100);
stepansnigirev 0:f43431023689 406 qr.position(0, 100);
stepansnigirev 0:f43431023689 407 qr.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 408
stepansnigirev 0:f43431023689 409 Button btn(nextCallback, "Next address");
stepansnigirev 0:f43431023689 410 btn.size(gui.width()/3-20, 80);
stepansnigirev 0:f43431023689 411 btn.position(gui.width()*2/3 + 10, gui.height()-100);
stepansnigirev 0:f43431023689 412
stepansnigirev 0:f43431023689 413 Button btn2(prevCallback, "Previous address");
stepansnigirev 0:f43431023689 414 btn2.size(gui.width()/3-20, 80);
stepansnigirev 0:f43431023689 415 btn2.position(10, gui.height()-100);
stepansnigirev 0:f43431023689 416
stepansnigirev 0:f43431023689 417 Button btn3(changeCallback, "Toggle\nchange");
stepansnigirev 0:f43431023689 418 btn3.size(gui.width()/3-20, 80);
stepansnigirev 0:f43431023689 419 btn3.position(gui.width()/3 + 10, gui.height()-100);
stepansnigirev 0:f43431023689 420
stepansnigirev 0:f43431023689 421 Button btn4(xpubCallback, "Show xpub");
stepansnigirev 0:f43431023689 422 btn4.size(gui.width()/2-20, 80);
stepansnigirev 0:f43431023689 423 btn4.position(gui.width()/2+10, gui.height()-200);
stepansnigirev 0:f43431023689 424
stepansnigirev 0:f43431023689 425 Button btn5(toMenuCallback, "Menu");
stepansnigirev 0:f43431023689 426 btn5.size(gui.width()/2-20, 80);
stepansnigirev 0:f43431023689 427 btn5.position(10, gui.height()-200);
stepansnigirev 0:f43431023689 428 }
stepansnigirev 0:f43431023689 429
stepansnigirev 0:f43431023689 430 static lv_res_t toMenuCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 431 showMenu();
stepansnigirev 0:f43431023689 432 return LV_RES_OK;
stepansnigirev 0:f43431023689 433 }
stepansnigirev 0:f43431023689 434
stepansnigirev 0:f43431023689 435 static lv_res_t showAddressesCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 436 showAddressScreen();
stepansnigirev 0:f43431023689 437 showAddress(child_index, change);
stepansnigirev 0:f43431023689 438 return LV_RES_OK;
stepansnigirev 0:f43431023689 439 }
stepansnigirev 0:f43431023689 440
stepansnigirev 0:f43431023689 441 static lv_res_t wipeCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 442 wipe();
stepansnigirev 0:f43431023689 443 return LV_RES_OK;
stepansnigirev 0:f43431023689 444 }
stepansnigirev 0:f43431023689 445
stepansnigirev 0:f43431023689 446 /*************** mnemonic stuff ***************/
stepansnigirev 0:f43431023689 447
stepansnigirev 0:f43431023689 448 static lv_res_t showMnemonicCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 449 string mnemonic = loadMnemonic();
stepansnigirev 0:f43431023689 450 showMessage("Here is your recovery phrase:", mnemonic);
stepansnigirev 0:f43431023689 451 return LV_RES_OK;
stepansnigirev 0:f43431023689 452 }
stepansnigirev 0:f43431023689 453
stepansnigirev 1:b88ef7630eb3 454 static const char * keys[] = {"q","w","e","r","t","y","u","i","o","p","\n",
stepansnigirev 1:b88ef7630eb3 455 "a","s","d","f","g","h","j","k","l","\n",
stepansnigirev 1:b88ef7630eb3 456 " ","z","x","c","v","b","n","m","<",""};
stepansnigirev 1:b88ef7630eb3 457
stepansnigirev 1:b88ef7630eb3 458 static lv_res_t typeCallback(lv_obj_t * btn, const char * key){
stepansnigirev 1:b88ef7630eb3 459 if(key[0] == '<'){
stepansnigirev 1:b88ef7630eb3 460 if(mnemonic.length() > 0){
stepansnigirev 1:b88ef7630eb3 461 mnemonic = mnemonic.substr(0,mnemonic.length()-1);
stepansnigirev 1:b88ef7630eb3 462 }
stepansnigirev 1:b88ef7630eb3 463 }else{
stepansnigirev 1:b88ef7630eb3 464 mnemonic += key;
stepansnigirev 1:b88ef7630eb3 465 }
stepansnigirev 1:b88ef7630eb3 466 dataLbl.text(mnemonic);
stepansnigirev 1:b88ef7630eb3 467 return LV_RES_OK;
stepansnigirev 1:b88ef7630eb3 468 }
stepansnigirev 1:b88ef7630eb3 469
stepansnigirev 0:f43431023689 470 static lv_res_t enterMnemonicCallback(lv_obj_t * btn){
stepansnigirev 1:b88ef7630eb3 471 gui.clear();
stepansnigirev 1:b88ef7630eb3 472 mnemonic = "";
stepansnigirev 1:b88ef7630eb3 473 titleLbl = Label("Enter your mnemonic");
stepansnigirev 1:b88ef7630eb3 474 titleLbl.size(gui.width(), 20);
stepansnigirev 1:b88ef7630eb3 475 titleLbl.position(0, 40);
stepansnigirev 1:b88ef7630eb3 476 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 1:b88ef7630eb3 477
stepansnigirev 1:b88ef7630eb3 478 dataLbl = Label(mnemonic);
stepansnigirev 1:b88ef7630eb3 479 dataLbl.size(gui.width()-100, 50);
stepansnigirev 1:b88ef7630eb3 480 dataLbl.position(50, 200);
stepansnigirev 1:b88ef7630eb3 481 dataLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 1:b88ef7630eb3 482
stepansnigirev 1:b88ef7630eb3 483 Keyboard kb(typeCallback, keys);
stepansnigirev 1:b88ef7630eb3 484 kb.size(gui.width(), gui.height()/3);
stepansnigirev 1:b88ef7630eb3 485 kb.position(0, gui.height()*2/3-150);
stepansnigirev 1:b88ef7630eb3 486
stepansnigirev 1:b88ef7630eb3 487 Button btnx(checkMnemonicCallback, "Continue");
stepansnigirev 1:b88ef7630eb3 488 btnx.size(gui.width()-100, 80);
stepansnigirev 1:b88ef7630eb3 489 btnx.position(50, gui.height()-100);
stepansnigirev 0:f43431023689 490 return LV_RES_OK;
stepansnigirev 0:f43431023689 491 }