def restore(self, t): if t == 'standard': text = self.enter_seed_dialog(MSG_ENTER_ANYTHING, None) if not text: return password = self.password_dialog() if Wallet.is_seed(text) or Wallet.is_xprv(text) or Wallet.is_private_key(text) else None wallet = Wallet.from_text(text, password, self.storage) elif re.match('(\d+)of(\d+)', t): n = int(re.match('(\d+)of(\d+)', t).group(2)) key_list = self.multi_seed_dialog(n - 1) if not key_list: return password = self.password_dialog() if any(map(lambda x: Wallet.is_seed(x) or Wallet.is_xprv(x), key_list)) else None wallet = Wallet.from_multisig(key_list, password, self.storage, t) else: self.storage.put('wallet_type', t, False) # call the constructor to load the plugin (side effect) Wallet(self.storage) wallet = always_hook('installwizard_restore', self, self.storage) if not wallet: util.print_error("no wallet") return # create first keys offline self.waiting_dialog(wallet.synchronize) return wallet
def verify_connection(self, expected_xfp, expected_xpub): ex = (expected_xfp, expected_xpub) if self._expected_device == ex: # all is as expected return if ( (self._expected_device is not None) or (self.dev.master_fingerprint != expected_xfp) or (self.dev.master_xpub != expected_xpub)): # probably indicating programing error, not hacking print_error("[coldcard]", f"xpubs. reported by device: {self.dev.master_xpub}. " f"stored in file: {expected_xpub}") raise RuntimeError("Expecting 0x%08x but that's not whats connected?!" % expected_xfp) # check signature over session key # - mitm might have lied about xfp and xpub up to here # - important that we use value capture at wallet creation time, not some value # we read over USB today self.dev.check_mitm(expected_xpub=expected_xpub) self._expected_device = ex print_error("[coldcard]", "Successfully verified against MiTM")
def do_full_push(self): try: bundle = {"labels": {}} for key, value in self.wallet.labels.iteritems(): encoded = self.encode(key) bundle["labels"][encoded] = self.encode(value) params = json.dumps(bundle) connection = httplib.HTTPConnection(self.target_host) connection.request("POST", ("/api/wallets/%s/labels/batch.json?auth_token=%s" % (self.wallet_id, self.auth_token())), params, {'Content-Type': 'application/json'}) response = connection.getresponse() if response.reason == httplib.responses[httplib.NOT_FOUND]: return try: response = json.loads(response.read()) except ValueError as e: return False if "error" in response: QMessageBox.warning(None, _("Error"),_("Could not sync labels: %s" % response["error"])) return False return True except socket.gaierror as e: print_error('Error connecting to service: %s ' % e) return False
def do_full_pull(self, force = False): try: connection = httplib.HTTPConnection(self.target_host) connection.request("GET", ("/api/wallets/%s/labels.json?auth_token=%s" % (self.wallet_id, self.auth_token())),"", {'Content-Type': 'application/json'}) response = connection.getresponse() if response.reason == httplib.responses[httplib.NOT_FOUND]: return try: response = json.loads(response.read()) except ValueError as e: return False if "error" in response: QMessageBox.warning(None, _("Error"),_("Could not sync labels: %s" % response["error"])) return False for label in response: decoded_key = self.decode(label["external_id"]) decoded_label = self.decode(label["text"]) if force or not self.wallet.labels.get(decoded_key): self.wallet.labels[decoded_key] = decoded_label return True except socket.gaierror as e: print_error('Error connecting to service: %s ' % e) return False
def close_wallet(self): print_error("trezor: clear session") if self.client: self.client.clear_session() self.client.transport.close() self.client = None self.wallet = None
def get_xpub(self, bip32_path): self.checkDevice() # bip32_path is of the form 44'/0'/1' # S-L-O-W - we don't handle the fingerprint directly, so compute # it manually from the previous node # This only happens once so it's bearable #self.get_client() # prompt for the PIN before displaying the dialog if necessary #self.handler.show_message("Computing master public key") try: splitPath = bip32_path.split('/') if splitPath[0] == 'm': splitPath = splitPath[1:] bip32_path = bip32_path[2:] fingerprint = 0 if len(splitPath) > 1: prevPath = "/".join(splitPath[0:len(splitPath) - 1]) nodeData = self.dongleObject.getWalletPublicKey(prevPath) publicKey = compress_public_key(nodeData['publicKey']) h = hashlib.new('ripemd160') h.update(hashlib.sha256(publicKey).digest()) fingerprint = unpack(">I", h.digest()[0:4])[0] nodeData = self.dongleObject.getWalletPublicKey(bip32_path) publicKey = compress_public_key(nodeData['publicKey']) depth = len(splitPath) lastChild = splitPath[len(splitPath) - 1].split('\'') if len(lastChild) == 1: childnum = int(lastChild[0]) else: childnum = 0x80000000 | int(lastChild[0]) xpub = bitcoin.serialize_xpub(0, str(nodeData['chainCode']), str(publicKey), depth, self.i4b(fingerprint), self.i4b(childnum)) return xpub except Exception as e: print_error(e) return None
def start_new_window(self, path, uri, app_is_starting=False): '''Raises the window for the wallet if it is open. Otherwise opens the wallet and creates a new window for it''' try: wallet = self.daemon.load_wallet(path, None) except BaseException as e: traceback.print_exc(file=sys.stdout) d = QMessageBox(QMessageBox.Warning, _('Error'), _('Cannot load wallet') + ' (1):\n' + str(e)) d.exec_() if app_is_starting: # do not return so that the wizard can appear wallet = None else: return if not wallet: storage = WalletStorage(path, manual_upgrades=True) wizard = InstallWizard(self.config, self.app, self.plugins, storage) try: wallet = wizard.run_and_get_wallet(self.daemon.get_wallet) except UserCancelled: pass except GoBack as e: print_error('[start_new_window] Exception caught (GoBack)', e) except (WalletFileException, BitcoinException) as e: traceback.print_exc(file=sys.stderr) d = QMessageBox(QMessageBox.Warning, _('Error'), _('Cannot load wallet') + ' (2):\n' + str(e)) d.exec_() return finally: wizard.terminate() if not wallet: return if not self.daemon.get_wallet(wallet.storage.path): # wallet was not in memory wallet.start_threads(self.daemon.network) self.daemon.add_wallet(wallet) try: for w in self.windows: if w.wallet.storage.path == wallet.storage.path: w.bring_to_top() return w = self.create_window_for_wallet(wallet) except BaseException as e: traceback.print_exc(file=sys.stdout) d = QMessageBox(QMessageBox.Warning, _('Error'), _('Cannot create window for wallet') + ':\n' + str(e)) d.exec_() return if uri: w.pay_to_URI(uri) w.bring_to_top() w.setWindowState(w.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) # this will activate the window w.activateWindow() return w
def give_error(self, message, clear_client = False): print_error(message) if not self.signing: self.handler.show_error(message) else: self.signing = False if clear_client: self.client = None raise Exception(message)
def give_error(self, message, clear_client=False): print_error(message) if not self.ux_busy: self.handler.show_error(message) else: self.ux_busy = False if clear_client: self.client = None raise UserFacingException(message)
def give_error(self, message, clear_client=False): print_error(message) if not self.signing: QMessageBox.warning(QDialog(), _("Warning"), _(message), _("OK")) else: self.signing = False if clear_client: self.plugin.client = None self.device_checked = False raise Exception(message)
def load_theme(self): """Load theme retrieved from wallet file.""" try: theme_prefix, theme_path = self.themes[self.theme_name] except KeyError: util.print_error("Theme not found!", self.theme_name) return QDir.setCurrent(os.path.join(theme_prefix, theme_path)) with open(rsrc("style.css")) as style_file: qApp.setStyleSheet(style_file.read())
def load_theme(self): """Load theme retrieved from wallet file.""" try: theme_prefix, theme_path = self.themes[self.theme_name] except KeyError: util.print_error("Theme not found!", self.theme_name) return full_theme_path = "%s/%s/style.css" % (theme_prefix, theme_path) with open(full_theme_path) as style_file: qApp.setStyleSheet(style_file.read())
def give_error(self, message, clear_client = False): print_error(message) if not self.signing: QMessageBox.warning(QDialog(), _('Warning'), _(message), _('OK')) else: self.signing = False if clear_client and self.client is not None: self.client.bad = True self.device_checked = False raise Exception(message)
def update(self): fx = self.parent.fx if fx: fx.history_used_spot = False r = self.wallet.get_full_history(domain=self.get_domain(), from_timestamp=None, to_timestamp=None, fx=fx) seen = set() history = fx.show_history() tx_list = list(self.transactions.values()) if r['transactions'] == tx_list: return if r['transactions'][:-1] == tx_list: print_error('history_list: one new transaction') row = r['transactions'][-1] txid = row['txid'] if txid not in self.transactions: self.transactions[txid] = row self.insert_tx(row) return else: print_error('history_list: tx added but txid is already in list (weird), txid: ', txid) for idx, row in enumerate(r['transactions']): txid = row['txid'] seen.add(txid) if txid not in self.transactions: self.transactions[txid] = row self.insert_tx(row) continue old = self.transactions[txid] if old == row: continue self.update_item(txid, self.wallet.get_tx_height(txid)) if history: self.update_fiat(txid, row) balance_str = self.parent.format_amount(row['balance'].value, whitespaces=True) self.txid_to_items[txid][4].setText(balance_str) self.txid_to_items[txid][4].setData(row['balance'].value, self.SORT_ROLE) old.clear() old.update(**row) removed = 0 l = list(enumerate(self.transactions.keys())) for idx, txid in l: if txid not in seen: del self.transactions[txid] del self.txid_to_items[txid] items = self.std_model.takeRow(idx - removed) removed_txid = items[0].data(self.TX_HASH_ROLE) assert removed_txid == txid, (idx, removed) removed += 1 self.apply_filter() # update summary self.summary = r['summary'] if not self.years and self.transactions: start_date = next(iter(self.transactions.values())).get('date') or date.today() end_date = next(iter(reversed(self.transactions.values()))).get('date') or date.today() self.years = [str(i) for i in range(start_date.year, end_date.year + 1)] self.period_combo.insertItems(1, self.years)
def set_dark_theme_if_needed(self): use_dark_theme = self.config.get('qt_gui_color_theme', 'default') == 'dark' if use_dark_theme: try: import qdarkstyle self.app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) except BaseException as e: use_dark_theme = False print_error('Error setting dark theme: {}'.format(e)) # Even if we ourselves don't set the dark theme, # the OS/window manager/etc might set *a dark theme*. # Hence, try to choose colors accordingly: ColorScheme.update_from_widget(QWidget(), force_dark=use_dark_theme)
def hid_send_plain(self, msg): reply = "" try: self.dbb_hid.write('\0' + bytearray(msg) + '\0' * (self.hidBufSize - len(msg))) r = [] while len(r) < self.hidBufSize: r = r + self.dbb_hid.read(self.hidBufSize) r = str(bytearray(r)).rstrip(' \t\r\n\0') r = r.replace("\0", '') reply = json.loads(r) except Exception as e: print_error('Exception caught ' + str(e)) return reply
def comserver_post_notification(self, payload): assert self.is_mobile_paired(), "unexpected mobile pairing error" url = 'https://digitalbitbox.com/smartverification/index.php' key_s = base64.b64decode(self.digitalbitbox_config[ENCRYPTION_PRIVKEY_KEY]) args = 'c=data&s=0&dt=0&uuid=%s&pl=%s' % ( self.digitalbitbox_config[CHANNEL_ID_KEY], EncodeAES_base64(key_s, json.dumps(payload).encode('ascii')).decode('ascii'), ) try: text = Network.send_http_on_proxy('post', url, body=args.encode('ascii'), headers={'content-type': 'application/x-www-form-urlencoded'}) print_error('digitalbitbox reply from server', text) except Exception as e: self.handler.show_error(repr(e)) # repr because str(Exception()) == ''
def hid_send_encrypt(self, msg): reply = "" try: secret = Hash(self.password) msg = EncodeAES(secret, msg) reply = self.hid_send_plain(msg) if 'ciphertext' in reply: reply = DecodeAES(secret, ''.join(reply["ciphertext"])) reply = json.loads(reply) if 'error' in reply: self.password = None except Exception as e: print_error('Exception caught ' + str(e)) return reply
def get_xpub(self, bip32_path, xtype): assert xtype in ColdcardPlugin.SUPPORTED_XTYPES print_error('[coldcard]', 'Derive xtype = %r' % xtype) xpub = self.dev.send_recv(CCProtocolPacker.get_xpub(bip32_path), timeout=5000) # TODO handle timeout? # change type of xpub to the requested type try: __, depth, fingerprint, child_number, c, cK = deserialize_xpub(xpub) except InvalidMasterKeyVersionBytes: raise UserFacingException(_('Invalid xpub magic. Make sure your {} device is set to the correct chain.') .format(self.device)) from None if xtype != 'standard': xpub = serialize_xpub(xtype, c, cK, depth, fingerprint, child_number) return xpub
def validate_dnssec(self, url): print_error('[OA] Checking DNSSEC trust chain for ' + url) try: default = dns.resolver.get_default_resolver() ns = default.nameservers[0] parts = url.split('.') for i in xrange(len(parts), 0, -1): sub = '.'.join(parts[i - 1:]) query = dns.message.make_query(sub, dns.rdatatype.NS) response = dns.query.udp(query, ns, 1) if response.rcode() != dns.rcode.NOERROR: return 0 if len(response.authority) > 0: rrset = response.authority[0] else: rrset = response.answer[0] rr = rrset[0] if rr.rdtype == dns.rdatatype.SOA: #Same server is authoritative, don't check again continue query = dns.message.make_query(sub, dns.rdatatype.DNSKEY, want_dnssec=True) response = dns.query.udp(query, ns, 1) if response.rcode() != 0: return 0 # HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD) # answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY) answer = response.answer if len(answer) != 2: return 0 # the DNSKEY should be self signed, validate it name = dns.name.from_text(sub) try: dns.dnssec.validate(answer[0], answer[1], {name: answer[0]}) except dns.dnssec.ValidationFailure: return 0 except Exception, e: return 0
def get_xpub(self, bip32_path, xtype): assert xtype in ColdcardPlugin.SUPPORTED_XTYPES print_error('[coldcard]', 'Derive xtype = %r' % xtype) xpub = self.dev.send_recv(CCProtocolPacker.get_xpub(bip32_path), timeout=5000) # TODO handle timeout? # change type of xpub to the requested type try: node = BIP32Node.from_xkey(xpub) except InvalidMasterKeyVersionBytes: raise UserFacingException(_('Invalid xpub magic. Make sure your {} device is set to the correct chain.') .format(self.device)) from None if xtype != 'standard': xpub = node._replace(xtype=xtype).to_xpub() return xpub
def set_label(self, item,label, changed): if not changed: return try: bundle = {"label": {"external_id": self.encode(item), "text": self.encode(label)}} params = json.dumps(bundle) connection = httplib.HTTPConnection(self.target_host) connection.request("POST", ("/api/wallets/%s/labels.json?auth_token=%s" % (self.wallet_id, self.auth_token())), params, {'Content-Type': 'application/json'}) response = connection.getresponse() if response.reason == httplib.responses[httplib.NOT_FOUND]: return response = json.loads(response.read()) except socket.gaierror as e: print_error('Error connecting to service: %s ' % e) return False
def use_tor_proxy(self, use_it): if not use_it: self.proxy_cb.setChecked(False) else: socks5_mode_index = self.proxy_mode.findText('SOCKS5') if socks5_mode_index == -1: print_error("[network_dialog] can't find proxy_mode 'SOCKS5'") return self.proxy_mode.setCurrentIndex(socks5_mode_index) self.proxy_host.setText("127.0.0.1") self.proxy_port.setText(str(self.tor_proxy[1])) self.proxy_user.setText("") self.proxy_password.setText("") self.tor_cb.setChecked(True) self.proxy_cb.setChecked(True) self.set_proxy()
def sign_transaction(self, tx, password): # Build a PSBT in memory, upload it for signing. # - we can also work offline (without paired device present) if tx.is_complete(): return client = self.get_client() assert client.dev.master_fingerprint == self.ckcc_xfp raw_psbt = self.build_psbt(tx) #open('debug.psbt', 'wb').write(out_fd.getvalue()) try: try: self.handler.show_message("Authorize Transaction...") client.sign_transaction_start(raw_psbt, True) while 1: # How to kill some time, without locking UI? time.sleep(0.250) resp = client.sign_transaction_poll() if resp is not None: break rlen, rsha = resp # download the resulting txn. new_raw = client.download_file(rlen, rsha) finally: self.handler.finished() except (CCUserRefused, CCBusyError) as exc: print_error('[coldcard]', 'Did not sign:', str(exc)) self.handler.show_error(str(exc)) return except BaseException as e: traceback.print_exc(file=sys.stderr) self.give_error(e, True) return # trust the coldcard to re-searilize final product right? tx.update(bh2u(new_raw))
def restore(self, t): if t == "standard": text = self.enter_seed_dialog(MSG_ENTER_ANYTHING, None) if not text: return if Wallet.is_xprv(text): password = self.password_dialog() wallet = Wallet.from_xprv(text, password, self.storage) elif Wallet.is_old_mpk(text): wallet = Wallet.from_old_mpk(text, self.storage) elif Wallet.is_xpub(text): wallet = Wallet.from_xpub(text, self.storage) elif Wallet.is_address(text): wallet = Wallet.from_address(text, self.storage) elif Wallet.is_private_key(text): password = self.password_dialog() wallet = Wallet.from_private_key(text, password, self.storage) elif Wallet.is_seed(text): password = self.password_dialog() wallet = Wallet.from_seed(text, password, self.storage) else: raise BaseException("unknown wallet type") elif re.match("(\d+)of(\d+)", t): n = int(re.match("(\d+)of(\d+)", t).group(2)) key_list = self.multi_seed_dialog(n - 1) if not key_list: return password = ( self.password_dialog() if any(map(lambda x: Wallet.is_seed(x) or Wallet.is_xprv(x), key_list)) else None ) wallet = Wallet.from_multisig(key_list, password, self.storage, t) else: self.storage.put("wallet_type", t, False) # call the constructor to load the plugin (side effect) Wallet(self.storage) wallet = always_hook("installwizard_restore", self, self.storage) if not wallet: util.print_error("no wallet") return # create first keys offline self.waiting_dialog(wallet.synchronize) return wallet
def run(self): self.is_running = True while self.is_running: if not self.keyhash: time.sleep(2) continue if not self.message: try: self.message = server.get(self.keyhash) except Exception as e: util.print_error("cannot contact cosigner pool") time.sleep(30) continue if self.message: self.parent.win.emit(SIGNAL("cosigner:receive")) # poll every 30 seconds time.sleep(30)
def start_new_window(self, path, uri): '''Raises the window for the wallet if it is open. Otherwise opens the wallet and creates a new window for it.''' for w in self.windows: if w.wallet.storage.path == path: w.bring_to_top() break else: try: wallet = self.daemon.load_wallet(path, None) except BaseException as e: traceback.print_exc(file=sys.stdout) d = QMessageBox(QMessageBox.Warning, _('Error'), _('Cannot load wallet:') + '\n' + str(e)) d.exec_() return if not wallet: storage = WalletStorage(path, manual_upgrades=True) wizard = InstallWizard(self.config, self.app, self.plugins, storage) try: wallet = wizard.run_and_get_wallet() except UserCancelled: pass except GoBack as e: print_error('[start_new_window] Exception caught (GoBack)', e) wizard.terminate() if not wallet: return wallet.start_threads(self.daemon.network) self.daemon.add_wallet(wallet) try: w = self.create_window_for_wallet(wallet) except BaseException as e: traceback.print_exc(file=sys.stdout) d = QMessageBox(QMessageBox.Warning, _('Error'), _('Cannot create window for wallet:') + '\n' + str(e)) d.exec_() return if uri: w.pay_to_URI(uri) w.bring_to_top() w.setWindowState(w.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) # this will activate the window w.activateWindow() return w
def restore(self, t): if t == 'standard': text = self.enter_seed_dialog(MSG_ENTER_ANYTHING, None) if not text: return if Wallet.is_xprv(text): password = self.password_dialog() wallet = Wallet.from_xprv(text, password, self.storage) elif Wallet.is_old_mpk(text): wallet = Wallet.from_old_mpk(text, self.storage) elif Wallet.is_xpub(text): wallet = Wallet.from_xpub(text, self.storage) elif Wallet.is_address(text): wallet = Wallet.from_address(text, self.storage) elif Wallet.is_private_key(text): password = self.password_dialog() wallet = Wallet.from_private_key(text, password, self.storage) elif Wallet.is_seed(text): password = self.password_dialog() wallet = Wallet.from_seed(text, password, self.storage) else: raise BaseException('unknown wallet type') elif t in ['2of2', '2of3']: key_list = self.multi_seed_dialog(1 if t == '2of2' else 2) if not key_list: return password = self.password_dialog() if any(map(lambda x: Wallet.is_seed(x) or Wallet.is_xprv(x), key_list)) else None wallet = Wallet.from_multisig(key_list, password, self.storage) else: self.storage.put('wallet_type', t, False) # call the constructor to load the plugin (side effect) Wallet(self.storage) wallet = always_hook('installwizard_restore', self, self.storage) if not wallet: util.print_error("no wallet") return # create first keys offline self.waiting_dialog(wallet.synchronize) return wallet
def run_recovery_dialog(): message = "Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet." dialog = gtk.MessageDialog( parent = None, flags = gtk.DIALOG_MODAL, buttons = gtk.BUTTONS_OK_CANCEL, message_format = message) vbox = dialog.vbox dialog.set_default_response(gtk.RESPONSE_OK) # ask seed, server and gap in the same dialog seed_box = gtk.HBox() seed_label = gtk.Label('Seed or mnemonic:') seed_label.set_size_request(150,-1) seed_box.pack_start(seed_label, False, False, 10) seed_label.show() seed_entry = gtk.Entry() seed_entry.show() seed_entry.set_size_request(450,-1) seed_box.pack_start(seed_entry, False, False, 10) add_help_button(seed_box, '.') seed_box.show() vbox.pack_start(seed_box, False, False, 5) dialog.show() r = dialog.run() seed = seed_entry.get_text() dialog.destroy() if r==gtk.RESPONSE_CANCEL: return False try: seed.decode('hex') except Exception: print_error("Warning: Not hex, trying decode") seed = mnemonic.mn_decode( seed.split(' ') ) if not seed: show_message("no seed") return False return seed
def hid_send_plain(self, msg): reply = "" try: serial_number = self.dbb_hid.get_serial_number_string() if "v2.0." in serial_number or "v1." in serial_number: hidBufSize = 4096 self.dbb_hid.write('\0' + msg + '\0' * (hidBufSize - len(msg))) r = bytearray() while len(r) < hidBufSize: r += bytearray(self.dbb_hid.read(hidBufSize)) else: self.hid_send_frame(msg) r = self.hid_read_frame() r = r.rstrip(b' \t\r\n\0') r = r.replace(b"\0", b'') reply = json.loads(r) except Exception as e: print_error('Exception caught ' + str(e)) return reply
def interface_changed(self): interface_text = self.interface_e.text() try: interface = json.loads(interface_text) constructor = {} for abi in interface: if abi.get('type') == 'constructor': constructor = abi break self.constructor = constructor if not constructor: self.args_e.setPlaceholderText('') return signature = '{}'.format(', '.join([ '{} {}'.format(i.get('type'), i.get('name')) for i in constructor.get('inputs', []) ])) self.args_e.setPlaceholderText(signature) except (BaseException, ) as e: self.constructor = {} self.args_e.setPlaceholderText('') print_error('[interface_changed]', str(e))
def start_new_window(self, path, uri): try: wallet = None #self.daemon.load_wallet(path, '1') except BaseException as e: traceback.print_exc(file=sys.stdout) print('Cannot load wallet: ' + str(e)) return if not wallet: storage = WalletStorage(path, manual_upgrades=True) wizard = InstallWizard(self.config, self.plugins, storage, self.daemon) def getWalletCallback(wallet): wizard.terminate() if not wallet: print('wallet not created. return ') return wallet.start_threads(self.daemon.network) print('adding wallet: ' + str(wallet)) self.daemon.add_wallet(wallet) def electrumWindowCallback(): print('electrumWindowCallback called') pass try: w = self.create_window_for_wallet(wallet, electrumWindowCallback) except BaseException as e: traceback.print_exc(file=sys.stdout) print('Cannot create window for wallet: ' + str(e)) try: wallet = wizard.run_and_get_wallet(getWalletCallback) except UserCancelled: pass except GoBack as e: print_error('[start_new_window] Exception caught (GoBack)', e)
def set_dark_theme_if_needed(self): use_dark_theme = self.config.get('qt_gui_color_theme', 'light') == 'dark' if use_dark_theme: try: file = QFile(":/dark.qss") file.open(QFile.ReadOnly | QFile.Text) stream = QTextStream(file) self.app.setStyleSheet(stream.readAll()) except BaseException as e: use_dark_theme = False print_error('Error setting dark theme: {}'.format(e)) else: try: file = QFile(":/light.qss") file.open(QFile.ReadOnly | QFile.Text) stream = QTextStream(file) self.app.setStyleSheet(stream.readAll()) except BaseException as e: print_error('Error setting light theme: {}'.format(e)) # Even if we ourselves don't set the dark theme, # the OS/window manager/etc might set *a dark theme*. # Hence, try to choose colors accordingly: ColorScheme.update_from_widget(QWidget(), force_dark=use_dark_theme)
def verify_connection(self, expected_xfp, expected_xpub): ex = (expected_xfp, expected_xpub) if self._expected_device == ex: # all is as expected return if ( (self._expected_device is not None) or (self.dev.master_fingerprint != expected_xfp) or (self.dev.master_xpub != expected_xpub)): # probably indicating programing error, not hacking raise RuntimeError("Expecting 0x%08x but that's not whats connected?!" % expected_xfp) # check signature over session key # - mitm might have lied about xfp and xpub up to here # - important that we use value capture at wallet creation time, not some value # we read over USB today self.dev.check_mitm(expected_xpub=expected_xpub) self._expected_device = ex print_error("[coldcard]", "Successfully verified against MiTM")
def hid_send_encrypt(self, msg): sha256_byte_len = 32 reply = "" try: encryption_key, authentication_key = derive_keys(self.password) msg = EncodeAES_bytes(encryption_key, msg) hmac_digest = hmac_oneshot(authentication_key, msg, hashlib.sha256) authenticated_msg = base64.b64encode(msg + hmac_digest) reply = self.hid_send_plain(authenticated_msg) if 'ciphertext' in reply: b64_unencoded = bytes(base64.b64decode(''.join(reply["ciphertext"]))) reply_hmac = b64_unencoded[-sha256_byte_len:] hmac_calculated = hmac_oneshot(authentication_key, b64_unencoded[:-sha256_byte_len], hashlib.sha256) if not hmac.compare_digest(reply_hmac, hmac_calculated): raise Exception("Failed to validate HMAC") reply = DecodeAES_bytes(encryption_key, b64_unencoded[:-sha256_byte_len]) reply = to_string(reply, 'utf8') reply = json.loads(reply) if 'error' in reply: self.password = None except Exception as e: print_error('Exception caught ' + repr(e)) return reply
def start_new_window(self, path, uri): '''Raises the window for the wallet if it is open. Otherwise opens the wallet and creates a new window for it.''' for w in self.windows: if w.wallet.storage.path == path: w.bring_to_top() break else: try: wallet = self.daemon.load_wallet(path, None) except BaseException as e: d = QMessageBox(QMessageBox.Warning, _('Error'), 'Cannot load wallet:\n' + str(e)) d.exec_() return if not wallet: storage = WalletStorage(path, manual_upgrades=True) wizard = InstallWizard(self.config, self.app, self.plugins, storage) try: wallet = wizard.run_and_get_wallet() except UserCancelled: pass except GoBack as e: print_error('[start_new_window] Exception caught (GoBack)', e) wizard.terminate() if not wallet: return wallet.start_threads(self.daemon.network) self.daemon.add_wallet(wallet) w = self.create_window_for_wallet(wallet) if uri: w.pay_to_URI(uri) w.bring_to_top() w.setWindowState(w.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) # this will activate the window w.activateWindow() return w
def set_label(self, item, label, changed): if not changed: return try: bundle = { "label": { "external_id": self.encode(item), "text": self.encode(label) } } params = json.dumps(bundle) connection = httplib.HTTPConnection(self.target_host) connection.request("POST", ("/api/wallets/%s/labels.json?auth_token=%s" % (self.wallet_id, self.auth_token())), params, {'Content-Type': 'application/json'}) response = connection.getresponse() if response.reason == httplib.responses[httplib.NOT_FOUND]: return response = json.loads(response.read()) except socket.gaierror as e: print_error('Error connecting to service: %s ' % e) return False
def digibox_sign(self, tx): try: change_keypath = None for i, txout in enumerate(tx.outputs): addr = tx.outputs[i][1] if self.is_change(addr): change_keypath = self.address_id(addr) require_pass = True; for i, txin in enumerate(tx.inputs): signatures = filter(None, txin['signatures']) num = txin['num_sig'] if len(signatures) == num: # Continue if this txin is complete. continue for x_pubkey in txin['x_pubkeys']: print_error("Creating signature for", x_pubkey) ii = tx.inputs[i]['x_pubkeys'].index(x_pubkey) keypath = self.address_id(tx.inputs[i]['address']) if True: for_sig = tx.tx_for_sig(i) msg = '{"sign": {"type":"transaction", "data":"%s", "keypath":"%s", "change_keypath":"%s"} }' % \ (for_sig, keypath, change_keypath) else: for_sig = Hash(tx.tx_for_sig(i).decode('hex')) for_sig = for_sig.encode('hex') msg = '{"sign": {"type":"hash", "data":"%s", "keypath":"%s"} }' % \ (for_sig, keypath) reply = self.commander(msg, require_pass) if reply==None: raise Exception("Could not sign transaction.") if 'sign' in reply: require_pass = False print_error("Adding signature for", x_pubkey) item = reply['sign'] tx.inputs[i]['x_pubkeys'][ii] = item['pubkey'] tx.inputs[i]['pubkeys'][ii] = item['pubkey'] r = int(item['sig'][:64], 16) s = int(item['sig'][64:], 16) sig = sigencode_der(r, s, generator_secp256k1.order()) tx.inputs[i]['signatures'][ii] = sig.encode('hex') else: raise Exception("Could not sign transaction.") except Exception as e: raise Exception(e) else: print_error("is_complete", tx.is_complete()) tx.raw = tx.serialize()
def update_status(self, b): print_error('hw device status', b)
def sign_transaction(self, tx, password): if tx.is_complete(): return try: p2pkhTransaction = True derivations = self.get_tx_derivations(tx) inputhasharray = [] hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin['type'] == 'coinbase': self.give_error( "Coinbase not supported") # should never happen if txin['type'] != 'p2pkh': p2pkhTransaction = False for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = Hash( binascii.unhexlify(tx.serialize_preimage(i))) hasharray_i = { 'hash': to_hexstr(inputHash), 'keypath': inputPath } hasharray.append(hasharray_i) inputhasharray.append(inputHash) break else: self.give_error("No matching x_key for sign_transaction" ) # should never happen # Build pubkeyarray from outputs for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if info is not None: index, xpubs, m = info changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = { 'pubkey': changePubkey, 'keypath': changePath } pubkeyarray.append(pubkeyarray_i) # Special serialization of the unsigned transaction for # the mobile verification app. # At the moment, verification only works for p2pkh transactions. if p2pkhTransaction: class CustomTXSerialization(Transaction): @classmethod def input_script(self, txin, estimate_size=False): if txin['type'] == 'p2pkh': return Transaction.get_preimage_script(txin) if txin['type'] == 'p2sh': # Multisig verification has partial support, but is disabled. This is the # expected serialization though, so we leave it here until we activate it. return '00' + push_script( Transaction.get_preimage_script(txin)) raise Exception("unsupported type %s" % txin['type']) tx_dbb_serialized = CustomTXSerialization( tx.serialize()).serialize() else: # We only need this for the signing echo / verification. tx_dbb_serialized = None # Build sign command dbb_signatures = [] steps = math.ceil(1.0 * len(hasharray) / self.maxInputs) for step in range(int(steps)): hashes = hasharray[step * self.maxInputs:(step + 1) * self.maxInputs] msg = { "sign": { "data": hashes, "checkpub": pubkeyarray, }, } if tx_dbb_serialized is not None: msg["sign"]["meta"] = to_hexstr(Hash(tx_dbb_serialized)) msg = json.dumps(msg).encode('ascii') dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) if 'error' in reply: raise Exception(reply['error']['message']) if 'echo' not in reply: raise Exception("Could not sign transaction.") if self.plugin.is_mobile_paired( ) and tx_dbb_serialized is not None: reply['tx'] = tx_dbb_serialized self.plugin.comserver_post_notification(reply) if steps > 1: self.handler.show_message( _("Signing large transaction. Please be patient ...") + "\n\n" + _("To continue, touch the Digital Bitbox's blinking light for 3 seconds." ) + " " + _("(Touch {} of {})").format((step + 1), steps) + "\n\n" + _("To cancel, briefly touch the blinking light or wait for the timeout." ) + "\n\n") else: self.handler.show_message( _("Signing transaction...") + "\n\n" + _("To continue, touch the Digital Bitbox's blinking light for 3 seconds." ) + "\n\n" + _("To cancel, briefly touch the blinking light or wait for the timeout." )) # Send twice, first returns an echo for smart verification reply = dbb_client.hid_send_encrypt(msg) self.handler.finished() if 'error' in reply: if reply["error"].get('code') in (600, 601): # aborted via LED short touch or timeout raise UserCancelled() raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") dbb_signatures.extend(reply['sign']) # Fill signatures if len(dbb_signatures) != len(tx.inputs()): raise Exception("Incorrect number of transactions signed." ) # Should never occur for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = list(filter(None, txin['signatures'])) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = dbb_signatures[i] if 'recid' in signed: # firmware > v2.1.1 recid = int(signed['recid'], 16) s = binascii.unhexlify(signed['sig']) h = inputhasharray[i] pk = MyVerifyingKey.from_signature(s, recid, h, curve=SECP256k1) pk = to_hexstr(point_to_ser(pk.pubkey.point, True)) elif 'pubkey' in signed: # firmware <= v2.1.1 pk = signed['pubkey'] if pk != pubkey: continue sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) txin['signatures'][ii] = to_hexstr(sig) + '01' tx._inputs[i] = txin except UserCancelled: raise except BaseException as e: self.give_error(e, True) else: print_error("Transaction is_complete", tx.is_complete()) tx.raw = tx.serialize()
def run_recovery_dialog(wallet): message = "Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet." dialog = gtk.MessageDialog(parent=None, flags=gtk.DIALOG_MODAL, buttons=gtk.BUTTONS_OK_CANCEL, message_format=message) vbox = dialog.vbox dialog.set_default_response(gtk.RESPONSE_OK) # ask seed, server and gap in the same dialog seed_box = gtk.HBox() seed_label = gtk.Label('Seed or mnemonic:') seed_label.set_size_request(150, -1) seed_box.pack_start(seed_label, False, False, 10) seed_label.show() seed_entry = gtk.Entry() seed_entry.show() seed_entry.set_size_request(450, -1) seed_box.pack_start(seed_entry, False, False, 10) add_help_button(seed_box, '.') seed_box.show() vbox.pack_start(seed_box, False, False, 5) gap = gtk.HBox() gap_label = gtk.Label('Gap limit:') gap_label.set_size_request(150, 10) gap_label.show() gap.pack_start(gap_label, False, False, 10) gap_entry = gtk.Entry() gap_entry.set_text("%d" % wallet.gap_limit) gap_entry.connect('changed', numbify, True) gap_entry.show() gap.pack_start(gap_entry, False, False, 10) add_help_button( gap, 'The maximum gap that is allowed between unused addresses in your wallet. During wallet recovery, this parameter is used to decide when to stop the recovery process. If you increase this value, you will need to remember it in order to be able to recover your wallet from seed.' ) gap.show() vbox.pack_start(gap, False, False, 5) dialog.show() r = dialog.run() gap = gap_entry.get_text() seed = seed_entry.get_text() dialog.destroy() if r == gtk.RESPONSE_CANCEL: return False try: gap = int(gap) except: show_message("error") return False try: seed.decode('hex') except: print_error("Warning: Not hex, trying decode") seed = mnemonic.mn_decode(seed.split(' ')) if not seed: show_message("no seed") return False return seed, gap
def show_error(self, msg, blocking=False): print_error(msg)
def do_full_push(self): try: bundle = {"labels": {}} for key, value in self.wallet.labels.iteritems(): try: encoded_key = self.encode(key) except: print_error('cannot encode', repr(key)) continue try: encoded_value = self.encode(value) except: print_error('cannot encode', repr(value)) continue bundle["labels"][encoded_key] = encoded_value params = json.dumps(bundle) connection = httplib.HTTPConnection(self.target_host) connection.request("POST", ("/api/wallets/%s/labels/batch.json?auth_token=%s" % (self.wallet_id, self.auth_token())), params, {'Content-Type': 'application/json'}) response = connection.getresponse() if response.reason == httplib.responses[httplib.NOT_FOUND]: print_error('404 error' % e) return try: response = json.loads(response.read()) except ValueError as e: print_error('Error loading labelsync response: %s' % e) return False if "error" in response: print_error('Error loading labelsync response.') return False except socket.gaierror as e: print_error('Error connecting to service: %s ' % e) return False self.window.labelsChanged.emit()
def close_wallet(self): print_error("trezor: clear session") if self.wallet and self.wallet.client: self.wallet.client.clear_session()
def run(self, action): if action == 'new': action, wallet_type = self.restore_or_create() self.storage.put('wallet_type', wallet_type, False) if action is None: return if action == 'restore': wallet = self.restore(wallet_type) if not wallet: return action = None else: wallet = Wallet(self.storage) action = wallet.get_action() # fixme: password is only needed for multiple accounts password = None while action is not None: util.print_error("installwizard:", wallet, action) if action == 'create_seed': seed = wallet.make_seed() if not self.show_seed(seed, None): return if not self.verify_seed(seed, None): return password = self.password_dialog() wallet.add_seed(seed, password) elif action == 'add_cosigner': xpub_hot = wallet.master_public_keys.get("m/") r = self.multi_mpk_dialog(xpub_hot, 1) if not r: return xpub_cold = r[0] wallet.add_master_public_key("cold/", xpub_cold) elif action == 'add_two_cosigners': xpub_hot = wallet.master_public_keys.get("m/") r = self.multi_mpk_dialog(xpub_hot, 2) if not r: return xpub1, xpub2 = r wallet.add_master_public_key("cold/", xpub1) wallet.add_master_public_key("remote/", xpub2) elif action == 'create_accounts': wallet.create_accounts(password) self.waiting_dialog(wallet.synchronize) elif action == 'create_cold_seed': self.create_cold_seed(wallet) return else: r = run_hook('install_wizard_action', self, wallet, action) if not r: raise BaseException('unknown wizard action', action) # next action action = wallet.get_action() if self.network: if self.network.interfaces: self.network_dialog() else: QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK')) self.network.stop() self.network = None # start wallet threads wallet.start_threads(self.network) if action == 'restore': self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText)) if self.network: if wallet.is_found(): QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK')) else: QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK')) else: QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK')) return wallet
def update_status(self, b): print_error('trezor status', b)
def run(self, action): if action == 'new': action, wallet_type = self.restore_or_create() if wallet_type == 'multisig': wallet_type = self.choice(_("Multi Signature Wallet"), 'Select wallet type', [('2of2', _("2 of 2")), ('2of3', _("2 of 3"))]) if not wallet_type: return elif wallet_type == 'hardware': hardware_wallets = map( lambda x: (x[1], x[2]), filter(lambda x: x[0] == 'hardware', electrum.wallet.wallet_types)) wallet_type = self.choice(_("Hardware Wallet"), 'Select your hardware wallet', hardware_wallets) if not wallet_type: return elif wallet_type == 'twofactor': wallet_type = '2fa' if action == 'create': self.storage.put('wallet_type', wallet_type, False) if action is None: return if action == 'restore': wallet = self.restore(wallet_type) if not wallet: return action = None else: wallet = Wallet(self.storage) action = wallet.get_action() # fixme: password is only needed for multiple accounts password = None while action is not None: util.print_error("installwizard:", wallet, action) if action == 'create_seed': seed = wallet.make_seed() if not self.show_seed(seed, None): return if not self.verify_seed(seed, None): return password = self.password_dialog() wallet.add_seed(seed, password) wallet.create_master_keys(password) elif action == 'add_cosigner': xpub1 = wallet.master_public_keys.get("x1/") r = self.multi_mpk_dialog(xpub1, 1) if not r: return xpub2 = r[0] wallet.add_master_public_key("x2/", xpub2) elif action == 'add_two_cosigners': xpub1 = wallet.master_public_keys.get("x1/") r = self.multi_mpk_dialog(xpub1, 2) if not r: return xpub2, xpub3 = r wallet.add_master_public_key("x2/", xpub2) wallet.add_master_public_key("x3/", xpub3) elif action == 'create_accounts': wallet.create_main_account(password) self.waiting_dialog(wallet.synchronize) else: f = run_hook('get_wizard_action', self, wallet, action) if not f: raise BaseException('unknown wizard action', action) r = f(wallet, self) if not r: return # next action action = wallet.get_action() if self.network: if self.network.interfaces: self.network_dialog() else: QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK')) self.network.stop() self.network = None # start wallet threads wallet.start_threads(self.network) if action == 'restore': self.waiting_dialog( lambda: wallet.restore(self.waiting_label.setText)) if self.network: msg = _("Recovery successful") if wallet.is_found() else _( "No transactions found for this seed") else: msg = _( "This wallet was restored offline. It may contain more addresses than displayed." ) QMessageBox.information(None, _('Information'), msg, _('OK')) return wallet
def start_new_window(self, path, uri, app_is_starting=False): '''Raises the window for the wallet if it is open. Otherwise opens the wallet and creates a new window for it''' try: wallet = self.daemon.load_wallet(path, None) except BaseException as e: traceback.print_exc(file=sys.stdout) d = QMessageBox(QMessageBox.Warning, _('Error'), _('Cannot load wallet') + ' (1):\n' + str(e)) d.exec_() if app_is_starting: # do not return so that the wizard can appear wallet = None else: return if not wallet: storage = WalletStorage(path, manual_upgrades=True) wizard = InstallWizard(self.config, self.app, self.plugins, storage) try: wallet = wizard.run_and_get_wallet(self.daemon.get_wallet) except UserCancelled: pass except GoBack as e: print_error('[start_new_window] Exception caught (GoBack)', e) except (WalletFileException, BitcoinException) as e: traceback.print_exc(file=sys.stderr) d = QMessageBox(QMessageBox.Warning, _('Error'), _('Cannot load wallet') + ' (2):\n' + str(e)) d.exec_() return finally: wizard.terminate() if not wallet: return if not self.daemon.get_wallet(wallet.storage.path): # wallet was not in memory wallet.start_threads(self.daemon.network) self.daemon.add_wallet(wallet) try: for w in self.windows: if w.wallet.storage.path == wallet.storage.path: w.bring_to_top() return w = self.create_window_for_wallet(wallet) except BaseException as e: traceback.print_exc(file=sys.stdout) d = QMessageBox( QMessageBox.Warning, _('Error'), _('Cannot create window for wallet') + ':\n' + str(e)) d.exec_() return if uri: w.pay_to_URI(uri) w.bring_to_top() w.setWindowState(w.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) # this will activate the window w.activateWindow() return w
from PyQt4.QtGui import * from PyQt4.QtCore import * import traceback import zlib import json from io import BytesIO import sys import platform try: import amodem.audio import amodem.main import amodem.config print_error('Audio MODEM is available.') amodem.log.addHandler(amodem.logging.StreamHandler(sys.stderr)) amodem.log.setLevel(amodem.logging.INFO) except ImportError: amodem = None print_error('Audio MODEM is not found.') class Plugin(BasePlugin): def __init__(self, parent, config, name): BasePlugin.__init__(self, parent, config, name) if self.is_available(): self.modem_config = amodem.config.slowest() self.library_name = {'Linux': 'libportaudio.so'}[platform.system()] def is_available(self):
def perform_hw1_preflight(self): try: firmwareInfo = self.dongleObject.getFirmwareVersion() firmware = firmwareInfo['version'].split(".") self.multiOutputSupported = int(firmware[0]) >= 1 and int( firmware[1]) >= 1 and int(firmware[2]) >= 4 self.segwitSupported = (int(firmware[0]) >= 1 and int(firmware[1]) >= 1 and int(firmware[2]) >= 10) or ( firmwareInfo['specialVersion'] == 0x20 and int(firmware[0]) == 1 and int(firmware[1]) == 0 and int(firmware[2]) >= 4) self.nativeSegwitSupported = int(firmware[0]) >= 1 and int( firmware[1]) >= 1 and int(firmware[2]) >= 10 if not checkFirmware(firmware): self.dongleObject.dongle.close() raise Exception( "HW1 firmware version too old. Please update at https://www.ledgerwallet.com" ) try: self.dongleObject.getOperationMode() except BTChipException as e: print_error('perform_hw1_preflight ex1', e) if (e.sw == 0x6985): self.dongleObject.dongle.close() self.handler.get_setup() # Acquire the new client on the next run else: raise e if self.has_detached_pin_support( self.dongleObject) and not self.is_pin_validated( self.dongleObject) and (self.handler is not None): remaining_attempts = self.dongleObject.getVerifyPinRemainingAttempts( ) if remaining_attempts != 1: msg = "Enter your Ledger PIN - remaining attempts : " + str( remaining_attempts) else: msg = "Enter your Ledger PIN - WARNING : LAST ATTEMPT. If the PIN is not correct, the dongle will be wiped." confirmed, p, pin = self.password_dialog(msg) if not confirmed: raise Exception( 'Aborted by user - please unplug the dongle and plug it again before retrying' ) pin = pin.encode() self.dongleObject.verifyPin(pin) # self.dongleObject.setAlternateCoinVersion(qtum.ADDRTYPE_P2PKH, qtum.ADDRTYPE_P2SH) except BTChipException as e: if (e.sw == 0x6700): return if (e.sw == 0x6faa): raise Exception( "Dongle is temporarily locked - please unplug it and replug it again" ) if ((e.sw & 0xFFF0) == 0x63c0): raise Exception( "Invalid PIN - please unplug the dongle and plug it again before retrying" ) print('perform_hw1_preflight ex2', e) raise e
def run_wallet_type(self, action, wallet_type): if action in ['create', 'restore']: if wallet_type == 'multisig': wallet_type = self.multisig_choice() if not wallet_type: return elif wallet_type == 'hardware': hardware_wallets = [] for item in electrum.wallet.wallet_types: t, name, description, loader = item if t == 'hardware': try: p = loader() except: util.print_error("cannot load plugin for:", name) continue if p: hardware_wallets.append((name, description)) wallet_type = self.choice(_("Hardware Wallet"), 'Select your hardware wallet', hardware_wallets) if not wallet_type: return elif wallet_type == 'twofactor': wallet_type = '2fa' if action == 'create': self.storage.put('wallet_type', wallet_type, False) if action is None: return if action == 'restore': wallet = self.restore(wallet_type) if not wallet: return action = None else: wallet = Wallet(self.storage) action = wallet.get_action() # fixme: password is only needed for multiple accounts password = None # load wallet in plugins always_hook('installwizard_load_wallet', wallet, self) while action is not None: util.print_error("installwizard:", wallet, action) if action == 'create_seed': lang = self.config.get('language') seed = wallet.make_seed(lang) if not self.show_seed(seed, None): return if not self.verify_seed(seed, None): return password = self.password_dialog() wallet.add_seed(seed, password) wallet.create_master_keys(password) elif action == 'add_cosigners': n = int(re.match('(\d+)of(\d+)', wallet.wallet_type).group(2)) xpub1 = wallet.master_public_keys.get("x1/") r = self.multi_mpk_dialog(xpub1, n - 1) if not r: return for i, xpub in enumerate(r): wallet.add_master_public_key("x%d/" % (i + 2), xpub) elif action == 'create_accounts': wallet.create_main_account(password) self.waiting_dialog(wallet.synchronize) else: f = always_hook('get_wizard_action', self, wallet, action) if not f: raise BaseException('unknown wizard action', action) r = f(wallet, self) if not r: return # next action action = wallet.get_action() if self.network: if self.network.interfaces: self.network_dialog() else: QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK')) self.network.stop() self.network = None # start wallet threads wallet.start_threads(self.network) if action == 'restore': self.waiting_dialog( lambda: wallet.restore(self.waiting_label.setText)) if self.network: msg = _("Recovery successful") if wallet.is_found() else _( "No transactions found for this seed") else: msg = _( "This wallet was restored offline. It may contain more addresses than displayed." ) QMessageBox.information(None, _('Information'), msg, _('OK')) return wallet
def sign_transaction(self, tx, password): if tx.is_complete(): return try: p2shTransaction = False derivations = self.get_tx_derivations(tx) inputhasharray = [] hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin['type'] == 'coinbase': self.give_error( "Coinbase not supported") # should never happen if txin['type'] in ['p2sh']: p2shTransaction = True for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = Hash( binascii.unhexlify(tx.serialize_preimage(i))) hasharray_i = { 'hash': to_hexstr(inputHash), 'keypath': inputPath } hasharray.append(hasharray_i) inputhasharray.append(inputHash) break else: self.give_error("No matching x_key for sign_transaction" ) # should never happen # Sanity check if p2shTransaction: for txinput in tx.inputs(): if txinput['type'] != 'p2sh': self.give_error( "P2SH / regular input mixed in same transaction not supported" ) # should never happen # Build pubkeyarray from outputs (unused because echo for smart verification not implemented) if not p2shTransaction: for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if info is not None: index, xpubs, m = info changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = { 'pubkey': changePubkey, 'keypath': changePath } pubkeyarray.append(pubkeyarray_i) # Build sign command dbb_signatures = [] steps = math.ceil(1.0 * len(hasharray) / self.maxInputs) for step in range(int(steps)): hashes = hasharray[step * self.maxInputs:(step + 1) * self.maxInputs] msg = ('{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \ (to_hexstr(Hash(tx.serialize())), json.dumps(hashes), json.dumps(pubkeyarray))).encode('utf8') dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) if 'error' in reply: raise Exception(reply['error']['message']) if 'echo' not in reply: raise Exception("Could not sign transaction.") if steps > 1: self.handler.show_message(_("Signing large transaction. Please be patient ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds. " \ "(Touch " + str(step + 1) + " of " + str(int(steps)) + ")\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.\r\n\r\n")) else: self.handler.show_message(_("Signing transaction ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.")) reply = dbb_client.hid_send_encrypt( msg ) # Send twice, first returns an echo for smart verification (not implemented) self.handler.clear_dialog() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") dbb_signatures.extend(reply['sign']) # Fill signatures if len(dbb_signatures) != len(tx.inputs()): raise Exception("Incorrect number of transactions signed." ) # Should never occur for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = list(filter(None, txin['signatures'])) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = dbb_signatures[i] if 'recid' in signed: # firmware > v2.1.1 recid = int(signed['recid'], 16) s = binascii.unhexlify(signed['sig']) h = inputhasharray[i] pk = MyVerifyingKey.from_signature(s, recid, h, curve=SECP256k1) pk = to_hexstr(point_to_ser(pk.pubkey.point, True)) elif 'pubkey' in signed: # firmware <= v2.1.1 pk = signed['pubkey'] if pk != pubkey: continue sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) txin['signatures'][ii] = to_hexstr(sig) + '01' tx._inputs[i] = txin except BaseException as e: self.give_error(e, True) else: print_error("Transaction is_complete", tx.is_complete()) tx.raw = tx.serialize()
def __init__(self, gui, name): print_error('[OA] Initialiasing OpenAlias plugin, OA_READY is ' + str(OA_READY)) BasePlugin.__init__(self, gui, name) self._is_available = OA_READY
def update(self): fx = self.parent.fx if fx: fx.history_used_spot = False r = self.wallet.get_full_history(domain=self.get_domain(), from_timestamp=None, to_timestamp=None, fx=fx) seen = set() history = fx.show_history() tx_list = list(self.transactions.values()) if r['transactions'] == tx_list: return if r['transactions'][:-1] == tx_list: print_error('history_list: one new transaction') row = r['transactions'][-1] txid = row['txid'] if txid not in self.transactions: self.transactions[txid] = row self.insert_tx(row) return else: print_error( 'history_list: tx added but txid is already in list (weird), txid: ', txid) for idx, row in enumerate(r['transactions']): txid = row['txid'] seen.add(txid) if txid not in self.transactions: self.transactions[txid] = row self.insert_tx(row) continue old = self.transactions[txid] if old == row: continue self.update_item(txid, self.wallet.get_tx_height(txid)) if history: self.update_fiat(txid, row) balance_str = self.parent.format_amount(row['balance'].value, whitespaces=True) self.txid_to_items[txid][4].setText(balance_str) self.txid_to_items[txid][4].setData(row['balance'].value, self.SORT_ROLE) old.clear() old.update(**row) removed = 0 l = list(enumerate(self.transactions.keys())) for idx, txid in l: if txid not in seen: del self.transactions[txid] del self.txid_to_items[txid] items = self.std_model.takeRow(idx - removed) removed_txid = items[0].data(self.TX_HASH_ROLE) assert removed_txid == txid, (idx, removed) removed += 1 self.apply_filter() # update summary self.summary = r['summary'] if not self.years and self.transactions: start_date = next(iter( self.transactions.values())).get('date') or date.today() end_date = next(iter(reversed( self.transactions.values()))).get('date') or date.today() self.years = [ str(i) for i in range(start_date.year, end_date.year + 1) ] self.period_combo.insertItems(1, self.years)
def give_error(message): print_error(message) raise Exception(message)
from electrum.address_synchronizer import TX_HEIGHT_LOCAL from electrum.i18n import _ from electrum.util import (block_explorer_URL, profiler, print_error, TxMinedInfo, OrderedDictWithIndex, PrintError) from .util import * if TYPE_CHECKING: from electrum.wallet import Abstract_Wallet try: from electrum.plot import plot_history, NothingToPlotException except: print_error( "qt/history_list: could not import electrum.plot. This feature needs matplotlib to be installed." ) plot_history = None # note: this list needs to be kept in sync with another in kivy TX_ICONS = [ "unconfirmed.png", "warning.png", "unconfirmed.png", "offline_tx.png", "clock1.png", "clock2.png", "clock3.png", "clock4.png", "clock5.png", "confirmed.png",
def sign_transaction(self, tx, password): if tx.is_complete(): return try: p2shTransaction = False derivations = self.get_tx_derivations(tx) hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin.get('is_coinbase'): self.give_error("Coinbase not supported") # should never happen if txin['type'] in ['p2sh']: p2shTransaction = True for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = Hash(tx.serialize_preimage(i).decode('hex')).encode('hex') hasharray_i = {'hash': inputHash, 'keypath': inputPath} hasharray.append(hasharray_i) break else: self.give_error("No matching x_key for sign_transaction") # should never happen # Sanity check if p2shTransaction: for txinput in tx.inputs(): if txinput['type'] != 'p2sh': self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen # Build pubkeyarray from outputs (unused because echo for smart verification not implemented) if not p2shTransaction: for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if info is not None: index, xpubs, m = info changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = {'pubkey': changePubkey, 'keypath': changePath} pubkeyarray.append(pubkeyarray_i) # Build sign command msg = '{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \ (Hash(tx.serialize()).encode('hex'), json.dumps(hasharray), json.dumps(pubkeyarray)) dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) self.handler.show_message(_("Signing transaction ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.")) reply = dbb_client.hid_send_encrypt(msg) # Send twice, first returns an echo for smart verification (not implemented) self.handler.clear_dialog() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") if len(reply['sign']) <> len(tx.inputs()): raise Exception("Incorrect number of transactions signed.") # Should never occur # Fill signatures for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = filter(None, txin['signatures']) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = reply['sign'][i] if signed['pubkey'] != pubkey: continue sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) txin['signatures'][ii] = sig.encode('hex') tx._inputs[i] = txin except BaseException as e: self.give_error(e, True) else: print_error("Transaction is_complete", tx.is_complete()) tx.raw = tx.serialize()