
Airgapped hardware wallet based on STM32F469-Discovery board using SD card to pass transaction data
Dependencies: mbed QSPI_DISCO_F469NI BSP_DISCO_F469NI
main.cpp@2:120e0ca2f3a7, 2019-07-23 (annotated)
- Committer:
- stepansnigirev
- Date:
- Tue Jul 23 14:06:50 2019 +0000
- Revision:
- 2:120e0ca2f3a7
- Parent:
- 1:b88ef7630eb3
- Child:
- 4:73e20d662d73
psbt improvement
Who changed what in which revision?
User | Revision | Line number | New 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 | 0:f43431023689 | 5 | #include "Bitcoin.h" |
stepansnigirev | 0:f43431023689 | 6 | #include "PSBT.h" |
stepansnigirev | 0:f43431023689 | 7 | |
stepansnigirev | 0:f43431023689 | 8 | /*********** forward declarations **************/ |
stepansnigirev | 0:f43431023689 | 9 | |
stepansnigirev | 0:f43431023689 | 10 | // generates a new mnemonic |
stepansnigirev | 0:f43431023689 | 11 | string generateNewMnemonic(); |
stepansnigirev | 0:f43431023689 | 12 | |
stepansnigirev | 0:f43431023689 | 13 | // generates hd keys from the mnemonic |
stepansnigirev | 0:f43431023689 | 14 | void initKeys(const string mnemonic, const string password = ""); |
stepansnigirev | 0:f43431023689 | 15 | |
stepansnigirev | 0:f43431023689 | 16 | /****************** GUI classes ****************/ |
stepansnigirev | 0:f43431023689 | 17 | |
stepansnigirev | 0:f43431023689 | 18 | Label titleLbl; |
stepansnigirev | 0:f43431023689 | 19 | Label dataLbl; |
stepansnigirev | 0:f43431023689 | 20 | QR qr; |
stepansnigirev | 0:f43431023689 | 21 | |
stepansnigirev | 0:f43431023689 | 22 | Button btn; |
stepansnigirev | 0:f43431023689 | 23 | Button printBtn; |
stepansnigirev | 0:f43431023689 | 24 | Label lbl; |
stepansnigirev | 0:f43431023689 | 25 | |
stepansnigirev | 0:f43431023689 | 26 | /***************** GUI functions ***************/ |
stepansnigirev | 0:f43431023689 | 27 | |
stepansnigirev | 0:f43431023689 | 28 | // if mnemonic is not present we can generate or recover it |
stepansnigirev | 0:f43431023689 | 29 | void showInitScreen(); |
stepansnigirev | 0:f43431023689 | 30 | // if mnemonic is there we go directly to the main menu |
stepansnigirev | 0:f43431023689 | 31 | void showMenu(); |
stepansnigirev | 0:f43431023689 | 32 | // handy function to display information to the user |
stepansnigirev | 0:f43431023689 | 33 | // with a title, a message and OK button that goes to the menu |
stepansnigirev | 0:f43431023689 | 34 | void showMessage(const string title, const string message); |
stepansnigirev | 0:f43431023689 | 35 | static lv_res_t toMenuCallback(lv_obj_t * btn); |
stepansnigirev | 0:f43431023689 | 36 | static lv_res_t showAddressesCallback(lv_obj_t * btn); |
stepansnigirev | 0:f43431023689 | 37 | static lv_res_t saveXpubCallback(lv_obj_t * btn); |
stepansnigirev | 0:f43431023689 | 38 | static lv_res_t signPSBTCallback(lv_obj_t * btn); |
stepansnigirev | 0:f43431023689 | 39 | static lv_res_t showMnemonicCallback(lv_obj_t * btn); |
stepansnigirev | 0:f43431023689 | 40 | static lv_res_t wipeCallback(lv_obj_t * btn); |
stepansnigirev | 0:f43431023689 | 41 | static lv_res_t newMnemonicCallback(lv_obj_t * btn); |
stepansnigirev | 0:f43431023689 | 42 | static lv_res_t enterMnemonicCallback(lv_obj_t * btn); |
stepansnigirev | 0:f43431023689 | 43 | void showAddress(unsigned int child_index, bool change); |
stepansnigirev | 0:f43431023689 | 44 | |
stepansnigirev | 0:f43431023689 | 45 | /***************** bitcoin keys ***************/ |
stepansnigirev | 0:f43431023689 | 46 | |
stepansnigirev | 1:b88ef7630eb3 | 47 | string mnemonic; |
stepansnigirev | 0:f43431023689 | 48 | HDPrivateKey root; // root private key |
stepansnigirev | 0:f43431023689 | 49 | HDPrivateKey account; // account master private key |
stepansnigirev | 0:f43431023689 | 50 | HDPublicKey xpub; // account master public key |
stepansnigirev | 0:f43431023689 | 51 | bool change = false; // internal or external address |
stepansnigirev | 0:f43431023689 | 52 | unsigned int child_index = 0; // current child index |
stepansnigirev | 0:f43431023689 | 53 | |
stepansnigirev | 0:f43431023689 | 54 | PSBT psbt; // psbt transaction we will be signing |
stepansnigirev | 0:f43431023689 | 55 | |
stepansnigirev | 0:f43431023689 | 56 | /******************* Main part *****************/ |
stepansnigirev | 0:f43431023689 | 57 | |
stepansnigirev | 0:f43431023689 | 58 | int main(){ |
stepansnigirev | 0:f43431023689 | 59 | init(); |
stepansnigirev | 0:f43431023689 | 60 | |
stepansnigirev | 0:f43431023689 | 61 | string mnemonic = loadMnemonic(); |
stepansnigirev | 0:f43431023689 | 62 | if(mnemonic.length() == 0){ |
stepansnigirev | 0:f43431023689 | 63 | showInitScreen(); |
stepansnigirev | 0:f43431023689 | 64 | }else{ |
stepansnigirev | 0:f43431023689 | 65 | initKeys(mnemonic); |
stepansnigirev | 0:f43431023689 | 66 | showMenu(); |
stepansnigirev | 0:f43431023689 | 67 | } |
stepansnigirev | 0:f43431023689 | 68 | |
stepansnigirev | 0:f43431023689 | 69 | while(1){ |
stepansnigirev | 0:f43431023689 | 70 | gui.update(); |
stepansnigirev | 0:f43431023689 | 71 | } |
stepansnigirev | 0:f43431023689 | 72 | } |
stepansnigirev | 0:f43431023689 | 73 | |
stepansnigirev | 0:f43431023689 | 74 | /****************** GUI stuff *****************/ |
stepansnigirev | 0:f43431023689 | 75 | void showInitScreen(){ |
stepansnigirev | 0:f43431023689 | 76 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 77 | titleLbl = Label("Let's set it up!"); |
stepansnigirev | 0:f43431023689 | 78 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 79 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 80 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 81 | |
stepansnigirev | 0:f43431023689 | 82 | Button btn(newMnemonicCallback, "Generate new mnemonic"); |
stepansnigirev | 0:f43431023689 | 83 | btn.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 84 | btn.position(0, 200); |
stepansnigirev | 0:f43431023689 | 85 | btn.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 86 | |
stepansnigirev | 0:f43431023689 | 87 | Button btn2(enterMnemonicCallback, "Enter existing mnemonic"); |
stepansnigirev | 0:f43431023689 | 88 | btn2.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 89 | btn2.position(0, 300); |
stepansnigirev | 0:f43431023689 | 90 | btn2.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 91 | } |
stepansnigirev | 0:f43431023689 | 92 | |
stepansnigirev | 0:f43431023689 | 93 | void showMenu(){ |
stepansnigirev | 0:f43431023689 | 94 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 95 | titleLbl = Label("What do you want to do?"); |
stepansnigirev | 0:f43431023689 | 96 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 97 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 98 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 99 | |
stepansnigirev | 0:f43431023689 | 100 | Button btn(showAddressesCallback, "Show addresses"); |
stepansnigirev | 0:f43431023689 | 101 | btn.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 102 | btn.position(0, 100); |
stepansnigirev | 0:f43431023689 | 103 | btn.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 104 | |
stepansnigirev | 0:f43431023689 | 105 | Button btn2(saveXpubCallback, "Export xpub"); |
stepansnigirev | 0:f43431023689 | 106 | btn2.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 107 | btn2.position(0, 300); |
stepansnigirev | 0:f43431023689 | 108 | btn2.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 109 | |
stepansnigirev | 0:f43431023689 | 110 | Button btn3(signPSBTCallback, "Sign PSBT transaction"); |
stepansnigirev | 0:f43431023689 | 111 | btn3.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 112 | btn3.position(0, 400); |
stepansnigirev | 0:f43431023689 | 113 | btn3.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 114 | |
stepansnigirev | 0:f43431023689 | 115 | Button btn4(wipeCallback, "Wipe device"); |
stepansnigirev | 0:f43431023689 | 116 | btn4.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 117 | btn4.position(0, 600); |
stepansnigirev | 0:f43431023689 | 118 | btn4.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 119 | |
stepansnigirev | 0:f43431023689 | 120 | Button btn5(showMnemonicCallback, "Show mnemonic"); |
stepansnigirev | 0:f43431023689 | 121 | btn5.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 122 | btn5.position(0, 700); |
stepansnigirev | 0:f43431023689 | 123 | btn5.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 124 | } |
stepansnigirev | 0:f43431023689 | 125 | |
stepansnigirev | 0:f43431023689 | 126 | void showMessage(const string title, const string message){ |
stepansnigirev | 0:f43431023689 | 127 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 128 | titleLbl = Label(title); |
stepansnigirev | 0:f43431023689 | 129 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 130 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 131 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 132 | |
stepansnigirev | 0:f43431023689 | 133 | dataLbl = Label(message); |
stepansnigirev | 0:f43431023689 | 134 | dataLbl.size(gui.width()-100, 100); |
stepansnigirev | 0:f43431023689 | 135 | dataLbl.position(50, 300); |
stepansnigirev | 0:f43431023689 | 136 | dataLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 137 | |
stepansnigirev | 0:f43431023689 | 138 | Button btn(toMenuCallback, "OK"); |
stepansnigirev | 0:f43431023689 | 139 | btn.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 140 | btn.position(0, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 141 | btn.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 142 | } |
stepansnigirev | 0:f43431023689 | 143 | |
stepansnigirev | 0:f43431023689 | 144 | static lv_res_t nextCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 145 | child_index++; |
stepansnigirev | 0:f43431023689 | 146 | showAddress(child_index, change); |
stepansnigirev | 0:f43431023689 | 147 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 148 | } |
stepansnigirev | 0:f43431023689 | 149 | static lv_res_t prevCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 150 | if(child_index > 0){ |
stepansnigirev | 0:f43431023689 | 151 | child_index--; |
stepansnigirev | 0:f43431023689 | 152 | showAddress(child_index, change); |
stepansnigirev | 0:f43431023689 | 153 | } |
stepansnigirev | 0:f43431023689 | 154 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 155 | } |
stepansnigirev | 0:f43431023689 | 156 | static lv_res_t changeCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 157 | change = !change; |
stepansnigirev | 0:f43431023689 | 158 | showAddress(child_index, change); |
stepansnigirev | 0:f43431023689 | 159 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 160 | } |
stepansnigirev | 0:f43431023689 | 161 | static lv_res_t xpubCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 162 | titleLbl.text("Your master public key"); |
stepansnigirev | 0:f43431023689 | 163 | qr.text(xpub.toString()); |
stepansnigirev | 0:f43431023689 | 164 | dataLbl.text(xpub.toString()); |
stepansnigirev | 0:f43431023689 | 165 | qr.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 166 | dataLbl.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 167 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 168 | } |
stepansnigirev | 0:f43431023689 | 169 | |
stepansnigirev | 0:f43431023689 | 170 | void showAddress(unsigned int child_index, bool change){ |
stepansnigirev | 0:f43431023689 | 171 | stringstream title; |
stepansnigirev | 0:f43431023689 | 172 | title << "Your "; |
stepansnigirev | 0:f43431023689 | 173 | if(change){ |
stepansnigirev | 0:f43431023689 | 174 | title << "change "; |
stepansnigirev | 0:f43431023689 | 175 | }else{ |
stepansnigirev | 0:f43431023689 | 176 | title << "receiving "; |
stepansnigirev | 0:f43431023689 | 177 | } |
stepansnigirev | 0:f43431023689 | 178 | title << "address #" << child_index << ":"; |
stepansnigirev | 0:f43431023689 | 179 | titleLbl.text(title.str()); |
stepansnigirev | 0:f43431023689 | 180 | string address = xpub.child(change).child(child_index).address(); |
stepansnigirev | 0:f43431023689 | 181 | dataLbl.text(address); |
stepansnigirev | 0:f43431023689 | 182 | dataLbl.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 183 | qr.text(string("bitcoin:") + address); |
stepansnigirev | 0:f43431023689 | 184 | qr.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 185 | } |
stepansnigirev | 0:f43431023689 | 186 | |
stepansnigirev | 0:f43431023689 | 187 | void showAddressScreen(){ |
stepansnigirev | 0:f43431023689 | 188 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 189 | titleLbl = Label("Your address"); |
stepansnigirev | 0:f43431023689 | 190 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 191 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 192 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 193 | |
stepansnigirev | 0:f43431023689 | 194 | dataLbl = Label(" "); |
stepansnigirev | 0:f43431023689 | 195 | dataLbl.size(gui.width()-100, 100); // full width |
stepansnigirev | 0:f43431023689 | 196 | dataLbl.position(50, gui.height()-300); |
stepansnigirev | 0:f43431023689 | 197 | dataLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 198 | |
stepansnigirev | 0:f43431023689 | 199 | qr = QR(" "); |
stepansnigirev | 0:f43431023689 | 200 | qr.size(gui.width()-100); |
stepansnigirev | 0:f43431023689 | 201 | qr.position(0, 100); |
stepansnigirev | 0:f43431023689 | 202 | qr.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 203 | |
stepansnigirev | 0:f43431023689 | 204 | Button btn(nextCallback, "Next address"); |
stepansnigirev | 0:f43431023689 | 205 | btn.size(gui.width()/3-20, 80); |
stepansnigirev | 0:f43431023689 | 206 | btn.position(gui.width()*2/3 + 10, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 207 | |
stepansnigirev | 0:f43431023689 | 208 | Button btn2(prevCallback, "Previous address"); |
stepansnigirev | 0:f43431023689 | 209 | btn2.size(gui.width()/3-20, 80); |
stepansnigirev | 0:f43431023689 | 210 | btn2.position(10, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 211 | |
stepansnigirev | 0:f43431023689 | 212 | Button btn3(changeCallback, "Toggle\nchange"); |
stepansnigirev | 0:f43431023689 | 213 | btn3.size(gui.width()/3-20, 80); |
stepansnigirev | 0:f43431023689 | 214 | btn3.position(gui.width()/3 + 10, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 215 | |
stepansnigirev | 0:f43431023689 | 216 | Button btn4(xpubCallback, "Show xpub"); |
stepansnigirev | 0:f43431023689 | 217 | btn4.size(gui.width()/2-20, 80); |
stepansnigirev | 0:f43431023689 | 218 | btn4.position(gui.width()/2+10, gui.height()-200); |
stepansnigirev | 0:f43431023689 | 219 | |
stepansnigirev | 0:f43431023689 | 220 | Button btn5(toMenuCallback, "Menu"); |
stepansnigirev | 0:f43431023689 | 221 | btn5.size(gui.width()/2-20, 80); |
stepansnigirev | 0:f43431023689 | 222 | btn5.position(10, gui.height()-200); |
stepansnigirev | 0:f43431023689 | 223 | } |
stepansnigirev | 0:f43431023689 | 224 | |
stepansnigirev | 0:f43431023689 | 225 | static lv_res_t toMenuCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 226 | showMenu(); |
stepansnigirev | 0:f43431023689 | 227 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 228 | } |
stepansnigirev | 0:f43431023689 | 229 | |
stepansnigirev | 0:f43431023689 | 230 | static lv_res_t showAddressesCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 231 | showAddressScreen(); |
stepansnigirev | 0:f43431023689 | 232 | showAddress(child_index, change); |
stepansnigirev | 0:f43431023689 | 233 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 234 | } |
stepansnigirev | 0:f43431023689 | 235 | static lv_res_t saveXpubCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 236 | // TODO: |
stepansnigirev | 0:f43431023689 | 237 | // - check if SD card present |
stepansnigirev | 0:f43431023689 | 238 | // - save xpub to the "xpub.txt" |
stepansnigirev | 0:f43431023689 | 239 | // - check if write was sucessful |
stepansnigirev | 0:f43431023689 | 240 | // - show success / fail message |
stepansnigirev | 0:f43431023689 | 241 | if(!SD.detected()){ |
stepansnigirev | 0:f43431023689 | 242 | showMessage("Error", "SD card is not present"); |
stepansnigirev | 0:f43431023689 | 243 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 244 | } |
stepansnigirev | 0:f43431023689 | 245 | int res = SD.save("xpub.txt", xpub.toString()); |
stepansnigirev | 0:f43431023689 | 246 | if(res != SD_SUCCESS){ |
stepansnigirev | 0:f43431023689 | 247 | showMessage("Error", "Something wrong with SD card"); |
stepansnigirev | 0:f43431023689 | 248 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 249 | } |
stepansnigirev | 0:f43431023689 | 250 | showMessage("Success","Master public key is saved to the SD card"); |
stepansnigirev | 0:f43431023689 | 251 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 252 | } |
stepansnigirev | 0:f43431023689 | 253 | static lv_res_t signConfirmCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 254 | if(!SD.detected()){ |
stepansnigirev | 0:f43431023689 | 255 | showMessage("Error", "SD card is not present"); |
stepansnigirev | 0:f43431023689 | 256 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 257 | } |
stepansnigirev | 0:f43431023689 | 258 | psbt.sign(account); |
stepansnigirev | 0:f43431023689 | 259 | |
stepansnigirev | 0:f43431023689 | 260 | uint8_t * raw = (uint8_t *)calloc(psbt.length(), sizeof(uint8_t)); |
stepansnigirev | 0:f43431023689 | 261 | size_t len = psbt.serialize(raw, psbt.length()); |
stepansnigirev | 0:f43431023689 | 262 | psbt = PSBT(); |
stepansnigirev | 2:120e0ca2f3a7 | 263 | string b64 = toBase64(raw, len); |
stepansnigirev | 0:f43431023689 | 264 | |
stepansnigirev | 2:120e0ca2f3a7 | 265 | int res = SD.save("signed.psbt", b64.c_str()); |
stepansnigirev | 0:f43431023689 | 266 | if(res != SD_SUCCESS){ |
stepansnigirev | 0:f43431023689 | 267 | showMessage("Error", "Something wrong with SD card"); |
stepansnigirev | 0:f43431023689 | 268 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 269 | } |
stepansnigirev | 0:f43431023689 | 270 | showMessage("Success", "Signed transaction saved to the SD card"); |
stepansnigirev | 0:f43431023689 | 271 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 272 | } |
stepansnigirev | 0:f43431023689 | 273 | void showSignRequest(){ |
stepansnigirev | 0:f43431023689 | 274 | stringstream ss; |
stepansnigirev | 0:f43431023689 | 275 | ss << "Sending:\n\n"; |
stepansnigirev | 0:f43431023689 | 276 | for(unsigned int i=0; i<psbt.tx.outputsNumber; i++){ |
stepansnigirev | 2:120e0ca2f3a7 | 277 | bool mine = psbt.isMine(i, account.xpub()); |
stepansnigirev | 0:f43431023689 | 278 | ss << psbt.tx.txOuts[i].address(&Testnet); |
stepansnigirev | 0:f43431023689 | 279 | if(mine){ |
stepansnigirev | 0:f43431023689 | 280 | ss << " (change)"; |
stepansnigirev | 0:f43431023689 | 281 | } |
stepansnigirev | 0:f43431023689 | 282 | ss << ": " << (float(psbt.tx.txOuts[i].amount)/1e5) << " mBTC\n\n"; |
stepansnigirev | 0:f43431023689 | 283 | } |
stepansnigirev | 0:f43431023689 | 284 | ss << "Fee: " << (float(psbt.fee())/1e5) << " mBTC"; |
stepansnigirev | 0:f43431023689 | 285 | |
stepansnigirev | 0:f43431023689 | 286 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 287 | titleLbl = Label("Sign transaction?"); |
stepansnigirev | 0:f43431023689 | 288 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 289 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 290 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 291 | |
stepansnigirev | 0:f43431023689 | 292 | dataLbl = Label(ss.str()); |
stepansnigirev | 0:f43431023689 | 293 | dataLbl.size(gui.width()-100, 100); |
stepansnigirev | 0:f43431023689 | 294 | dataLbl.position(50, 300); |
stepansnigirev | 0:f43431023689 | 295 | dataLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 296 | |
stepansnigirev | 0:f43431023689 | 297 | Button btn(toMenuCallback, "Cancel"); |
stepansnigirev | 0:f43431023689 | 298 | btn.size(gui.width()/2-45, 80); |
stepansnigirev | 0:f43431023689 | 299 | btn.position(30, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 300 | |
stepansnigirev | 0:f43431023689 | 301 | Button btn2(signConfirmCallback, "Confirm"); |
stepansnigirev | 0:f43431023689 | 302 | btn2.size(gui.width()/2-45, 80); |
stepansnigirev | 0:f43431023689 | 303 | btn2.position(gui.width()/2+30, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 304 | } |
stepansnigirev | 0:f43431023689 | 305 | static lv_res_t signPSBTCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 306 | if(!SD.detected()){ |
stepansnigirev | 0:f43431023689 | 307 | showMessage("Error", "SD card is not present"); |
stepansnigirev | 0:f43431023689 | 308 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 309 | } |
stepansnigirev | 0:f43431023689 | 310 | string s = SD.read("unsigned.psbt"); |
stepansnigirev | 0:f43431023689 | 311 | if(s.length() == 0){ |
stepansnigirev | 0:f43431023689 | 312 | showMessage("Fail", "Can't read unsigned.psbt file"); |
stepansnigirev | 0:f43431023689 | 313 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 314 | } |
stepansnigirev | 2:120e0ca2f3a7 | 315 | uint8_t * raw = new uint8_t[s.length()]; |
stepansnigirev | 2:120e0ca2f3a7 | 316 | unsigned int len = fromBase64(s.c_str(), s.length(), raw, s.length()); |
stepansnigirev | 0:f43431023689 | 317 | if(len == 0){ |
stepansnigirev | 2:120e0ca2f3a7 | 318 | delete [] raw; |
stepansnigirev | 0:f43431023689 | 319 | showMessage("Fail", "Can't convert from base64"); |
stepansnigirev | 0:f43431023689 | 320 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 321 | } |
stepansnigirev | 0:f43431023689 | 322 | psbt.reset(); |
stepansnigirev | 0:f43431023689 | 323 | len = psbt.parse(raw, len); |
stepansnigirev | 2:120e0ca2f3a7 | 324 | delete [] raw; |
stepansnigirev | 0:f43431023689 | 325 | if(len == 0){ |
stepansnigirev | 0:f43431023689 | 326 | showMessage("Fail", "Can't parse PSBT"); |
stepansnigirev | 0:f43431023689 | 327 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 328 | } |
stepansnigirev | 0:f43431023689 | 329 | showSignRequest(); |
stepansnigirev | 0:f43431023689 | 330 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 331 | } |
stepansnigirev | 0:f43431023689 | 332 | static lv_res_t wipeCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 333 | wipe(); |
stepansnigirev | 0:f43431023689 | 334 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 335 | } |
stepansnigirev | 0:f43431023689 | 336 | |
stepansnigirev | 0:f43431023689 | 337 | /*************** mnemonic stuff ***************/ |
stepansnigirev | 0:f43431023689 | 338 | |
stepansnigirev | 0:f43431023689 | 339 | static lv_res_t newMnemonicCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 340 | // TODO: |
stepansnigirev | 0:f43431023689 | 341 | // - generate random buffer (16 or 32 bytes) |
stepansnigirev | 0:f43431023689 | 342 | // - create a new mnemonic from it |
stepansnigirev | 0:f43431023689 | 343 | // - save this mnemonic |
stepansnigirev | 0:f43431023689 | 344 | // - display it to the user |
stepansnigirev | 0:f43431023689 | 345 | uint8_t randomBuffer[16]; |
stepansnigirev | 0:f43431023689 | 346 | getRandomBuffer(randomBuffer, sizeof(randomBuffer)); |
stepansnigirev | 0:f43431023689 | 347 | string mnemonic = generateMnemonic(randomBuffer, sizeof(randomBuffer)); |
stepansnigirev | 0:f43431023689 | 348 | saveMnemonic(mnemonic); |
stepansnigirev | 0:f43431023689 | 349 | showMessage("Write down your recovery phrase:", mnemonic); |
stepansnigirev | 0:f43431023689 | 350 | initKeys(mnemonic); |
stepansnigirev | 0:f43431023689 | 351 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 352 | } |
stepansnigirev | 0:f43431023689 | 353 | |
stepansnigirev | 0:f43431023689 | 354 | static lv_res_t showMnemonicCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 355 | string mnemonic = loadMnemonic(); |
stepansnigirev | 0:f43431023689 | 356 | showMessage("Here is your recovery phrase:", mnemonic); |
stepansnigirev | 0:f43431023689 | 357 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 358 | } |
stepansnigirev | 0:f43431023689 | 359 | |
stepansnigirev | 1:b88ef7630eb3 | 360 | static const char * keys[] = {"q","w","e","r","t","y","u","i","o","p","\n", |
stepansnigirev | 1:b88ef7630eb3 | 361 | "a","s","d","f","g","h","j","k","l","\n", |
stepansnigirev | 1:b88ef7630eb3 | 362 | " ","z","x","c","v","b","n","m","<",""}; |
stepansnigirev | 1:b88ef7630eb3 | 363 | |
stepansnigirev | 1:b88ef7630eb3 | 364 | static lv_res_t typeCallback(lv_obj_t * btn, const char * key){ |
stepansnigirev | 1:b88ef7630eb3 | 365 | if(key[0] == '<'){ |
stepansnigirev | 1:b88ef7630eb3 | 366 | if(mnemonic.length() > 0){ |
stepansnigirev | 1:b88ef7630eb3 | 367 | mnemonic = mnemonic.substr(0,mnemonic.length()-1); |
stepansnigirev | 1:b88ef7630eb3 | 368 | } |
stepansnigirev | 1:b88ef7630eb3 | 369 | }else{ |
stepansnigirev | 1:b88ef7630eb3 | 370 | mnemonic += key; |
stepansnigirev | 1:b88ef7630eb3 | 371 | } |
stepansnigirev | 1:b88ef7630eb3 | 372 | dataLbl.text(mnemonic); |
stepansnigirev | 1:b88ef7630eb3 | 373 | return LV_RES_OK; |
stepansnigirev | 1:b88ef7630eb3 | 374 | } |
stepansnigirev | 1:b88ef7630eb3 | 375 | |
stepansnigirev | 1:b88ef7630eb3 | 376 | static lv_res_t checkMnemonicCallback(lv_obj_t * btn){ |
stepansnigirev | 1:b88ef7630eb3 | 377 | if(checkMnemonic(mnemonic.c_str())){ |
stepansnigirev | 1:b88ef7630eb3 | 378 | saveMnemonic(mnemonic); |
stepansnigirev | 1:b88ef7630eb3 | 379 | initKeys(mnemonic); |
stepansnigirev | 1:b88ef7630eb3 | 380 | showMessage("Mnemonic is ok", "Recovered sucessfully"); |
stepansnigirev | 1:b88ef7630eb3 | 381 | }else{ |
stepansnigirev | 1:b88ef7630eb3 | 382 | mnemonic = ""; |
stepansnigirev | 1:b88ef7630eb3 | 383 | dataLbl.text(mnemonic); |
stepansnigirev | 1:b88ef7630eb3 | 384 | } |
stepansnigirev | 1:b88ef7630eb3 | 385 | return LV_RES_OK; |
stepansnigirev | 1:b88ef7630eb3 | 386 | } |
stepansnigirev | 1:b88ef7630eb3 | 387 | |
stepansnigirev | 0:f43431023689 | 388 | static lv_res_t enterMnemonicCallback(lv_obj_t * btn){ |
stepansnigirev | 1:b88ef7630eb3 | 389 | gui.clear(); |
stepansnigirev | 1:b88ef7630eb3 | 390 | mnemonic = ""; |
stepansnigirev | 1:b88ef7630eb3 | 391 | titleLbl = Label("Enter your mnemonic"); |
stepansnigirev | 1:b88ef7630eb3 | 392 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 1:b88ef7630eb3 | 393 | titleLbl.position(0, 40); |
stepansnigirev | 1:b88ef7630eb3 | 394 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 1:b88ef7630eb3 | 395 | |
stepansnigirev | 1:b88ef7630eb3 | 396 | dataLbl = Label(mnemonic); |
stepansnigirev | 1:b88ef7630eb3 | 397 | dataLbl.size(gui.width()-100, 50); |
stepansnigirev | 1:b88ef7630eb3 | 398 | dataLbl.position(50, 200); |
stepansnigirev | 1:b88ef7630eb3 | 399 | dataLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 1:b88ef7630eb3 | 400 | |
stepansnigirev | 1:b88ef7630eb3 | 401 | Keyboard kb(typeCallback, keys); |
stepansnigirev | 1:b88ef7630eb3 | 402 | kb.size(gui.width(), gui.height()/3); |
stepansnigirev | 1:b88ef7630eb3 | 403 | kb.position(0, gui.height()*2/3-150); |
stepansnigirev | 1:b88ef7630eb3 | 404 | |
stepansnigirev | 1:b88ef7630eb3 | 405 | Button btnx(checkMnemonicCallback, "Continue"); |
stepansnigirev | 1:b88ef7630eb3 | 406 | btnx.size(gui.width()-100, 80); |
stepansnigirev | 1:b88ef7630eb3 | 407 | btnx.position(50, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 408 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 409 | } |
stepansnigirev | 0:f43431023689 | 410 | |
stepansnigirev | 0:f43431023689 | 411 | void initKeys(const string mnemonic, const string password){ |
stepansnigirev | 0:f43431023689 | 412 | // TODO: |
stepansnigirev | 0:f43431023689 | 413 | // - derive root key from the mnemonic and empty password |
stepansnigirev | 0:f43431023689 | 414 | // - derive account key using m/84'/1'/0'/ derivation path |
stepansnigirev | 0:f43431023689 | 415 | // - get account master public key |
stepansnigirev | 0:f43431023689 | 416 | root.fromMnemonic(mnemonic, password); |
stepansnigirev | 0:f43431023689 | 417 | account = root.derive("m/84'/1'/0'/"); |
stepansnigirev | 0:f43431023689 | 418 | xpub = account.xpub(); |
stepansnigirev | 0:f43431023689 | 419 | } |