示例#1
0
 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
示例#2
0
    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")
示例#3
0
文件: labels.py 项目: arorts/electrum
    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
示例#4
0
文件: labels.py 项目: arorts/electrum
    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
示例#5
0
 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
示例#6
0
 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
示例#7
0
    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
示例#8
0
 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)
示例#9
0
 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)
示例#10
0
 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)
示例#11
0
 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())
示例#12
0
 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())
示例#13
0
 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)
示例#14
0
 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)
示例#15
0
 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)
示例#16
0
 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
示例#17
0
 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()) == ''
示例#18
0
 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
示例#19
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:
         __, 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
示例#20
0
    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
示例#21
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
示例#22
0
文件: labels.py 项目: arorts/electrum
    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
示例#23
0
 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()
示例#24
0
    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))
示例#25
0
    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
示例#26
0
 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)
示例#27
0
    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
示例#28
0
    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
示例#29
0
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
示例#30
0
 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
示例#31
0
 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))
示例#32
0
    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)
示例#33
0
 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)
示例#34
0
    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")
示例#35
0
 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
示例#36
0
    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
示例#37
0
 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
示例#38
0
    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
示例#39
0
    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()
示例#40
0
 def update_status(self, b):
     print_error('hw device status', b)
示例#41
0
    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()
示例#42
0
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
示例#43
0
 def show_error(self, msg, blocking=False):
     print_error(msg)
示例#44
0
    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()
示例#45
0
 def close_wallet(self):
     print_error("trezor: clear session")
     if self.wallet and self.wallet.client:
         self.wallet.client.clear_session()
示例#46
0
    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)
示例#48
0
    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
示例#49
0
    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
示例#50
0
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):
示例#51
0
    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
示例#52
0
    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
示例#53
0
    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()
示例#54
0
 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
示例#55
0
 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)
示例#56
0
def give_error(message):
    print_error(message)
    raise Exception(message)
示例#57
0
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",
示例#58
0
    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()