
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@0:f43431023689, 2019-07-20 (annotated)
- Committer:
- stepansnigirev
- Date:
- Sat Jul 20 15:16:38 2019 +0000
- Revision:
- 0:f43431023689
- Child:
- 1:b88ef7630eb3
working demo
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 | 0:f43431023689 | 47 | HDPrivateKey root; // root private key |
stepansnigirev | 0:f43431023689 | 48 | HDPrivateKey account; // account master private key |
stepansnigirev | 0:f43431023689 | 49 | HDPublicKey xpub; // account master public key |
stepansnigirev | 0:f43431023689 | 50 | bool change = false; // internal or external address |
stepansnigirev | 0:f43431023689 | 51 | unsigned int child_index = 0; // current child index |
stepansnigirev | 0:f43431023689 | 52 | |
stepansnigirev | 0:f43431023689 | 53 | PSBT psbt; // psbt transaction we will be signing |
stepansnigirev | 0:f43431023689 | 54 | |
stepansnigirev | 0:f43431023689 | 55 | /******************* Main part *****************/ |
stepansnigirev | 0:f43431023689 | 56 | |
stepansnigirev | 0:f43431023689 | 57 | int main(){ |
stepansnigirev | 0:f43431023689 | 58 | init(); |
stepansnigirev | 0:f43431023689 | 59 | |
stepansnigirev | 0:f43431023689 | 60 | string mnemonic = loadMnemonic(); |
stepansnigirev | 0:f43431023689 | 61 | if(mnemonic.length() == 0){ |
stepansnigirev | 0:f43431023689 | 62 | showInitScreen(); |
stepansnigirev | 0:f43431023689 | 63 | }else{ |
stepansnigirev | 0:f43431023689 | 64 | initKeys(mnemonic); |
stepansnigirev | 0:f43431023689 | 65 | showMenu(); |
stepansnigirev | 0:f43431023689 | 66 | } |
stepansnigirev | 0:f43431023689 | 67 | |
stepansnigirev | 0:f43431023689 | 68 | while(1){ |
stepansnigirev | 0:f43431023689 | 69 | gui.update(); |
stepansnigirev | 0:f43431023689 | 70 | } |
stepansnigirev | 0:f43431023689 | 71 | } |
stepansnigirev | 0:f43431023689 | 72 | |
stepansnigirev | 0:f43431023689 | 73 | /****************** GUI stuff *****************/ |
stepansnigirev | 0:f43431023689 | 74 | void showInitScreen(){ |
stepansnigirev | 0:f43431023689 | 75 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 76 | titleLbl = Label("Let's set it up!"); |
stepansnigirev | 0:f43431023689 | 77 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 78 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 79 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 80 | |
stepansnigirev | 0:f43431023689 | 81 | Button btn(newMnemonicCallback, "Generate new mnemonic"); |
stepansnigirev | 0:f43431023689 | 82 | btn.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 83 | btn.position(0, 200); |
stepansnigirev | 0:f43431023689 | 84 | btn.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 85 | |
stepansnigirev | 0:f43431023689 | 86 | Button btn2(enterMnemonicCallback, "Enter existing mnemonic"); |
stepansnigirev | 0:f43431023689 | 87 | btn2.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 88 | btn2.position(0, 300); |
stepansnigirev | 0:f43431023689 | 89 | btn2.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 90 | } |
stepansnigirev | 0:f43431023689 | 91 | |
stepansnigirev | 0:f43431023689 | 92 | void showMenu(){ |
stepansnigirev | 0:f43431023689 | 93 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 94 | titleLbl = Label("What do you want to do?"); |
stepansnigirev | 0:f43431023689 | 95 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 96 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 97 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 98 | |
stepansnigirev | 0:f43431023689 | 99 | Button btn(showAddressesCallback, "Show addresses"); |
stepansnigirev | 0:f43431023689 | 100 | btn.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 101 | btn.position(0, 100); |
stepansnigirev | 0:f43431023689 | 102 | btn.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 103 | |
stepansnigirev | 0:f43431023689 | 104 | Button btn2(saveXpubCallback, "Export xpub"); |
stepansnigirev | 0:f43431023689 | 105 | btn2.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 106 | btn2.position(0, 300); |
stepansnigirev | 0:f43431023689 | 107 | btn2.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 108 | |
stepansnigirev | 0:f43431023689 | 109 | Button btn3(signPSBTCallback, "Sign PSBT transaction"); |
stepansnigirev | 0:f43431023689 | 110 | btn3.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 111 | btn3.position(0, 400); |
stepansnigirev | 0:f43431023689 | 112 | btn3.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 113 | |
stepansnigirev | 0:f43431023689 | 114 | Button btn4(wipeCallback, "Wipe device"); |
stepansnigirev | 0:f43431023689 | 115 | btn4.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 116 | btn4.position(0, 600); |
stepansnigirev | 0:f43431023689 | 117 | btn4.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 118 | |
stepansnigirev | 0:f43431023689 | 119 | Button btn5(showMnemonicCallback, "Show mnemonic"); |
stepansnigirev | 0:f43431023689 | 120 | btn5.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 121 | btn5.position(0, 700); |
stepansnigirev | 0:f43431023689 | 122 | btn5.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 123 | } |
stepansnigirev | 0:f43431023689 | 124 | |
stepansnigirev | 0:f43431023689 | 125 | void showMessage(const string title, const string message){ |
stepansnigirev | 0:f43431023689 | 126 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 127 | titleLbl = Label(title); |
stepansnigirev | 0:f43431023689 | 128 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 129 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 130 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 131 | |
stepansnigirev | 0:f43431023689 | 132 | dataLbl = Label(message); |
stepansnigirev | 0:f43431023689 | 133 | dataLbl.size(gui.width()-100, 100); |
stepansnigirev | 0:f43431023689 | 134 | dataLbl.position(50, 300); |
stepansnigirev | 0:f43431023689 | 135 | dataLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 136 | |
stepansnigirev | 0:f43431023689 | 137 | Button btn(toMenuCallback, "OK"); |
stepansnigirev | 0:f43431023689 | 138 | btn.size(gui.width()-100, 80); |
stepansnigirev | 0:f43431023689 | 139 | btn.position(0, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 140 | btn.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 141 | } |
stepansnigirev | 0:f43431023689 | 142 | |
stepansnigirev | 0:f43431023689 | 143 | static lv_res_t nextCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 144 | child_index++; |
stepansnigirev | 0:f43431023689 | 145 | showAddress(child_index, change); |
stepansnigirev | 0:f43431023689 | 146 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 147 | } |
stepansnigirev | 0:f43431023689 | 148 | static lv_res_t prevCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 149 | if(child_index > 0){ |
stepansnigirev | 0:f43431023689 | 150 | child_index--; |
stepansnigirev | 0:f43431023689 | 151 | showAddress(child_index, change); |
stepansnigirev | 0:f43431023689 | 152 | } |
stepansnigirev | 0:f43431023689 | 153 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 154 | } |
stepansnigirev | 0:f43431023689 | 155 | static lv_res_t changeCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 156 | change = !change; |
stepansnigirev | 0:f43431023689 | 157 | showAddress(child_index, change); |
stepansnigirev | 0:f43431023689 | 158 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 159 | } |
stepansnigirev | 0:f43431023689 | 160 | static lv_res_t xpubCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 161 | titleLbl.text("Your master public key"); |
stepansnigirev | 0:f43431023689 | 162 | qr.text(xpub.toString()); |
stepansnigirev | 0:f43431023689 | 163 | dataLbl.text(xpub.toString()); |
stepansnigirev | 0:f43431023689 | 164 | qr.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 165 | dataLbl.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 166 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 167 | } |
stepansnigirev | 0:f43431023689 | 168 | |
stepansnigirev | 0:f43431023689 | 169 | void showAddress(unsigned int child_index, bool change){ |
stepansnigirev | 0:f43431023689 | 170 | stringstream title; |
stepansnigirev | 0:f43431023689 | 171 | title << "Your "; |
stepansnigirev | 0:f43431023689 | 172 | if(change){ |
stepansnigirev | 0:f43431023689 | 173 | title << "change "; |
stepansnigirev | 0:f43431023689 | 174 | }else{ |
stepansnigirev | 0:f43431023689 | 175 | title << "receiving "; |
stepansnigirev | 0:f43431023689 | 176 | } |
stepansnigirev | 0:f43431023689 | 177 | title << "address #" << child_index << ":"; |
stepansnigirev | 0:f43431023689 | 178 | titleLbl.text(title.str()); |
stepansnigirev | 0:f43431023689 | 179 | string address = xpub.child(change).child(child_index).address(); |
stepansnigirev | 0:f43431023689 | 180 | dataLbl.text(address); |
stepansnigirev | 0:f43431023689 | 181 | dataLbl.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 182 | qr.text(string("bitcoin:") + address); |
stepansnigirev | 0:f43431023689 | 183 | qr.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 184 | } |
stepansnigirev | 0:f43431023689 | 185 | |
stepansnigirev | 0:f43431023689 | 186 | void showAddressScreen(){ |
stepansnigirev | 0:f43431023689 | 187 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 188 | titleLbl = Label("Your address"); |
stepansnigirev | 0:f43431023689 | 189 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 190 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 191 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 192 | |
stepansnigirev | 0:f43431023689 | 193 | dataLbl = Label(" "); |
stepansnigirev | 0:f43431023689 | 194 | dataLbl.size(gui.width()-100, 100); // full width |
stepansnigirev | 0:f43431023689 | 195 | dataLbl.position(50, gui.height()-300); |
stepansnigirev | 0:f43431023689 | 196 | dataLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 197 | |
stepansnigirev | 0:f43431023689 | 198 | qr = QR(" "); |
stepansnigirev | 0:f43431023689 | 199 | qr.size(gui.width()-100); |
stepansnigirev | 0:f43431023689 | 200 | qr.position(0, 100); |
stepansnigirev | 0:f43431023689 | 201 | qr.align(ALIGN_CENTER); |
stepansnigirev | 0:f43431023689 | 202 | |
stepansnigirev | 0:f43431023689 | 203 | Button btn(nextCallback, "Next address"); |
stepansnigirev | 0:f43431023689 | 204 | btn.size(gui.width()/3-20, 80); |
stepansnigirev | 0:f43431023689 | 205 | btn.position(gui.width()*2/3 + 10, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 206 | |
stepansnigirev | 0:f43431023689 | 207 | Button btn2(prevCallback, "Previous address"); |
stepansnigirev | 0:f43431023689 | 208 | btn2.size(gui.width()/3-20, 80); |
stepansnigirev | 0:f43431023689 | 209 | btn2.position(10, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 210 | |
stepansnigirev | 0:f43431023689 | 211 | Button btn3(changeCallback, "Toggle\nchange"); |
stepansnigirev | 0:f43431023689 | 212 | btn3.size(gui.width()/3-20, 80); |
stepansnigirev | 0:f43431023689 | 213 | btn3.position(gui.width()/3 + 10, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 214 | |
stepansnigirev | 0:f43431023689 | 215 | Button btn4(xpubCallback, "Show xpub"); |
stepansnigirev | 0:f43431023689 | 216 | btn4.size(gui.width()/2-20, 80); |
stepansnigirev | 0:f43431023689 | 217 | btn4.position(gui.width()/2+10, gui.height()-200); |
stepansnigirev | 0:f43431023689 | 218 | |
stepansnigirev | 0:f43431023689 | 219 | Button btn5(toMenuCallback, "Menu"); |
stepansnigirev | 0:f43431023689 | 220 | btn5.size(gui.width()/2-20, 80); |
stepansnigirev | 0:f43431023689 | 221 | btn5.position(10, gui.height()-200); |
stepansnigirev | 0:f43431023689 | 222 | } |
stepansnigirev | 0:f43431023689 | 223 | |
stepansnigirev | 0:f43431023689 | 224 | static lv_res_t toMenuCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 225 | showMenu(); |
stepansnigirev | 0:f43431023689 | 226 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 227 | } |
stepansnigirev | 0:f43431023689 | 228 | |
stepansnigirev | 0:f43431023689 | 229 | static lv_res_t showAddressesCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 230 | showAddressScreen(); |
stepansnigirev | 0:f43431023689 | 231 | showAddress(child_index, change); |
stepansnigirev | 0:f43431023689 | 232 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 233 | } |
stepansnigirev | 0:f43431023689 | 234 | static lv_res_t saveXpubCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 235 | // TODO: |
stepansnigirev | 0:f43431023689 | 236 | // - check if SD card present |
stepansnigirev | 0:f43431023689 | 237 | // - save xpub to the "xpub.txt" |
stepansnigirev | 0:f43431023689 | 238 | // - check if write was sucessful |
stepansnigirev | 0:f43431023689 | 239 | // - show success / fail message |
stepansnigirev | 0:f43431023689 | 240 | if(!SD.detected()){ |
stepansnigirev | 0:f43431023689 | 241 | showMessage("Error", "SD card is not present"); |
stepansnigirev | 0:f43431023689 | 242 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 243 | } |
stepansnigirev | 0:f43431023689 | 244 | int res = SD.save("xpub.txt", xpub.toString()); |
stepansnigirev | 0:f43431023689 | 245 | if(res != SD_SUCCESS){ |
stepansnigirev | 0:f43431023689 | 246 | showMessage("Error", "Something wrong with SD card"); |
stepansnigirev | 0:f43431023689 | 247 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 248 | } |
stepansnigirev | 0:f43431023689 | 249 | showMessage("Success","Master public key is saved to the SD card"); |
stepansnigirev | 0:f43431023689 | 250 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 251 | } |
stepansnigirev | 0:f43431023689 | 252 | static lv_res_t signConfirmCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 253 | if(!SD.detected()){ |
stepansnigirev | 0:f43431023689 | 254 | showMessage("Error", "SD card is not present"); |
stepansnigirev | 0:f43431023689 | 255 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 256 | } |
stepansnigirev | 0:f43431023689 | 257 | psbt.sign(account); |
stepansnigirev | 0:f43431023689 | 258 | |
stepansnigirev | 0:f43431023689 | 259 | uint8_t * raw = (uint8_t *)calloc(psbt.length(), sizeof(uint8_t)); |
stepansnigirev | 0:f43431023689 | 260 | size_t len = psbt.serialize(raw, psbt.length()); |
stepansnigirev | 0:f43431023689 | 261 | psbt = PSBT(); |
stepansnigirev | 0:f43431023689 | 262 | // cout << len << endl; |
stepansnigirev | 0:f43431023689 | 263 | char * b64 = (char *)calloc(2*len+1, sizeof(char)); |
stepansnigirev | 0:f43431023689 | 264 | len = toBase64(raw, len, b64, 2*len+1); |
stepansnigirev | 0:f43431023689 | 265 | free(raw); |
stepansnigirev | 0:f43431023689 | 266 | |
stepansnigirev | 0:f43431023689 | 267 | int res = SD.save("signed.psbt", b64); |
stepansnigirev | 0:f43431023689 | 268 | free(b64); |
stepansnigirev | 0:f43431023689 | 269 | if(res != SD_SUCCESS){ |
stepansnigirev | 0:f43431023689 | 270 | showMessage("Error", "Something wrong with SD card"); |
stepansnigirev | 0:f43431023689 | 271 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 272 | } |
stepansnigirev | 0:f43431023689 | 273 | showMessage("Success", "Signed transaction saved to the SD card"); |
stepansnigirev | 0:f43431023689 | 274 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 275 | } |
stepansnigirev | 0:f43431023689 | 276 | void showSignRequest(){ |
stepansnigirev | 0:f43431023689 | 277 | stringstream ss; |
stepansnigirev | 0:f43431023689 | 278 | ss << "Sending:\n\n"; |
stepansnigirev | 0:f43431023689 | 279 | for(unsigned int i=0; i<psbt.tx.outputsNumber; i++){ |
stepansnigirev | 0:f43431023689 | 280 | // TODO: better to put it into PSBT method like check_output or something |
stepansnigirev | 0:f43431023689 | 281 | bool mine = false; |
stepansnigirev | 0:f43431023689 | 282 | if(psbt.txOutsMeta[i].derivationsLen > 0){ |
stepansnigirev | 0:f43431023689 | 283 | for(unsigned int j=0; j<psbt.txOutsMeta[i].derivationsLen; j++){ |
stepansnigirev | 0:f43431023689 | 284 | HDPrivateKey prv = account.derive(psbt.txOutsMeta[i].derivations[j].derivation, psbt.txOutsMeta[i].derivations[j].derivationLen); |
stepansnigirev | 0:f43431023689 | 285 | PublicKey pub = prv.publicKey(); |
stepansnigirev | 0:f43431023689 | 286 | // TODO: fix public key comparison |
stepansnigirev | 0:f43431023689 | 287 | if(pub.toString() == psbt.txOutsMeta[i].derivations[j].pubkey.toString()){ |
stepansnigirev | 0:f43431023689 | 288 | mine = true; |
stepansnigirev | 0:f43431023689 | 289 | } |
stepansnigirev | 0:f43431023689 | 290 | } |
stepansnigirev | 0:f43431023689 | 291 | } |
stepansnigirev | 0:f43431023689 | 292 | ss << psbt.tx.txOuts[i].address(&Testnet); |
stepansnigirev | 0:f43431023689 | 293 | if(mine){ |
stepansnigirev | 0:f43431023689 | 294 | ss << " (change)"; |
stepansnigirev | 0:f43431023689 | 295 | } |
stepansnigirev | 0:f43431023689 | 296 | ss << ": " << (float(psbt.tx.txOuts[i].amount)/1e5) << " mBTC\n\n"; |
stepansnigirev | 0:f43431023689 | 297 | } |
stepansnigirev | 0:f43431023689 | 298 | ss << "Fee: " << (float(psbt.fee())/1e5) << " mBTC"; |
stepansnigirev | 0:f43431023689 | 299 | |
stepansnigirev | 0:f43431023689 | 300 | gui.clear(); |
stepansnigirev | 0:f43431023689 | 301 | titleLbl = Label("Sign transaction?"); |
stepansnigirev | 0:f43431023689 | 302 | titleLbl.size(gui.width(), 20); |
stepansnigirev | 0:f43431023689 | 303 | titleLbl.position(0, 40); |
stepansnigirev | 0:f43431023689 | 304 | titleLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 305 | |
stepansnigirev | 0:f43431023689 | 306 | dataLbl = Label(ss.str()); |
stepansnigirev | 0:f43431023689 | 307 | dataLbl.size(gui.width()-100, 100); |
stepansnigirev | 0:f43431023689 | 308 | dataLbl.position(50, 300); |
stepansnigirev | 0:f43431023689 | 309 | dataLbl.alignText(ALIGN_TEXT_CENTER); |
stepansnigirev | 0:f43431023689 | 310 | |
stepansnigirev | 0:f43431023689 | 311 | Button btn(toMenuCallback, "Cancel"); |
stepansnigirev | 0:f43431023689 | 312 | btn.size(gui.width()/2-45, 80); |
stepansnigirev | 0:f43431023689 | 313 | btn.position(30, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 314 | |
stepansnigirev | 0:f43431023689 | 315 | Button btn2(signConfirmCallback, "Confirm"); |
stepansnigirev | 0:f43431023689 | 316 | btn2.size(gui.width()/2-45, 80); |
stepansnigirev | 0:f43431023689 | 317 | btn2.position(gui.width()/2+30, gui.height()-100); |
stepansnigirev | 0:f43431023689 | 318 | } |
stepansnigirev | 0:f43431023689 | 319 | static lv_res_t signPSBTCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 320 | if(!SD.detected()){ |
stepansnigirev | 0:f43431023689 | 321 | showMessage("Error", "SD card is not present"); |
stepansnigirev | 0:f43431023689 | 322 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 323 | } |
stepansnigirev | 0:f43431023689 | 324 | string s = SD.read("unsigned.psbt"); |
stepansnigirev | 0:f43431023689 | 325 | if(s.length() == 0){ |
stepansnigirev | 0:f43431023689 | 326 | showMessage("Fail", "Can't read unsigned.psbt file"); |
stepansnigirev | 0:f43431023689 | 327 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 328 | } |
stepansnigirev | 0:f43431023689 | 329 | uint8_t raw[1000]; |
stepansnigirev | 0:f43431023689 | 330 | unsigned int len = fromBase64(s.c_str(), s.length(), raw, sizeof(raw)); |
stepansnigirev | 0:f43431023689 | 331 | if(len == 0){ |
stepansnigirev | 0:f43431023689 | 332 | showMessage("Fail", "Can't convert from base64"); |
stepansnigirev | 0:f43431023689 | 333 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 334 | } |
stepansnigirev | 0:f43431023689 | 335 | psbt.reset(); |
stepansnigirev | 0:f43431023689 | 336 | len = psbt.parse(raw, len); |
stepansnigirev | 0:f43431023689 | 337 | if(len == 0){ |
stepansnigirev | 0:f43431023689 | 338 | showMessage("Fail", "Can't parse PSBT"); |
stepansnigirev | 0:f43431023689 | 339 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 340 | } |
stepansnigirev | 0:f43431023689 | 341 | showSignRequest(); |
stepansnigirev | 0:f43431023689 | 342 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 343 | } |
stepansnigirev | 0:f43431023689 | 344 | static lv_res_t wipeCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 345 | wipe(); |
stepansnigirev | 0:f43431023689 | 346 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 347 | } |
stepansnigirev | 0:f43431023689 | 348 | |
stepansnigirev | 0:f43431023689 | 349 | /*************** mnemonic stuff ***************/ |
stepansnigirev | 0:f43431023689 | 350 | |
stepansnigirev | 0:f43431023689 | 351 | static lv_res_t newMnemonicCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 352 | // TODO: |
stepansnigirev | 0:f43431023689 | 353 | // - generate random buffer (16 or 32 bytes) |
stepansnigirev | 0:f43431023689 | 354 | // - create a new mnemonic from it |
stepansnigirev | 0:f43431023689 | 355 | // - save this mnemonic |
stepansnigirev | 0:f43431023689 | 356 | // - display it to the user |
stepansnigirev | 0:f43431023689 | 357 | uint8_t randomBuffer[16]; |
stepansnigirev | 0:f43431023689 | 358 | getRandomBuffer(randomBuffer, sizeof(randomBuffer)); |
stepansnigirev | 0:f43431023689 | 359 | string mnemonic = generateMnemonic(randomBuffer, sizeof(randomBuffer)); |
stepansnigirev | 0:f43431023689 | 360 | saveMnemonic(mnemonic); |
stepansnigirev | 0:f43431023689 | 361 | showMessage("Write down your recovery phrase:", mnemonic); |
stepansnigirev | 0:f43431023689 | 362 | initKeys(mnemonic); |
stepansnigirev | 0:f43431023689 | 363 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 364 | } |
stepansnigirev | 0:f43431023689 | 365 | |
stepansnigirev | 0:f43431023689 | 366 | static lv_res_t showMnemonicCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 367 | string mnemonic = loadMnemonic(); |
stepansnigirev | 0:f43431023689 | 368 | showMessage("Here is your recovery phrase:", mnemonic); |
stepansnigirev | 0:f43431023689 | 369 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 370 | } |
stepansnigirev | 0:f43431023689 | 371 | |
stepansnigirev | 0:f43431023689 | 372 | static lv_res_t enterMnemonicCallback(lv_obj_t * btn){ |
stepansnigirev | 0:f43431023689 | 373 | return LV_RES_OK; |
stepansnigirev | 0:f43431023689 | 374 | } |
stepansnigirev | 0:f43431023689 | 375 | |
stepansnigirev | 0:f43431023689 | 376 | void initKeys(const string mnemonic, const string password){ |
stepansnigirev | 0:f43431023689 | 377 | // TODO: |
stepansnigirev | 0:f43431023689 | 378 | // - derive root key from the mnemonic and empty password |
stepansnigirev | 0:f43431023689 | 379 | // - derive account key using m/84'/1'/0'/ derivation path |
stepansnigirev | 0:f43431023689 | 380 | // - get account master public key |
stepansnigirev | 0:f43431023689 | 381 | root.fromMnemonic(mnemonic, password); |
stepansnigirev | 0:f43431023689 | 382 | account = root.derive("m/84'/1'/0'/"); |
stepansnigirev | 0:f43431023689 | 383 | xpub = account.xpub(); |
stepansnigirev | 0:f43431023689 | 384 | } |