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