예제 #1
0
class ElectrumGui:
    def __init__(self, config, network, plugins):
        self.network = network
        self.config = config
        storage = WalletStorage(self.config.get_wallet_path())
        if not storage.file_exists:
            raise BaseException("Wallet not found")
        self.wallet = Wallet(storage)
        self.cmd_runner = Commands(self.config, self.wallet, self.network)
        host = config.get('rpchost', 'localhost')
        port = config.get('rpcport', 7777)
        self.server = SimpleJSONRPCServer((host, port),
                                          requestHandler=RequestHandler)
        self.server.socket.settimeout(1)
        for cmdname in known_commands:
            self.server.register_function(getattr(self.cmd_runner, cmdname),
                                          cmdname)

    def main(self):
        self.wallet.start_threads(self.network)
        while True:
            try:
                self.server.handle_request()
            except socket.timeout:
                continue
            except:
                break
        self.wallet.stop_threads()
예제 #2
0
파일: jsonrpc.py 프로젝트: GemHQ/electrum
class ElectrumGui:

    def __init__(self, config, network):
        self.network = network
        self.config = config
        storage = WalletStorage(self.config.get_wallet_path())
        if not storage.file_exists:
            raise BaseException("Wallet not found")
        self.wallet = Wallet(storage)
        self.cmd_runner = Commands(self.config, self.wallet, self.network)
        host = config.get('rpchost', 'localhost')
        port = config.get('rpcport', 7777)
        self.server = SimpleJSONRPCServer((host, port), requestHandler=RequestHandler)
        self.server.socket.settimeout(1)
        for cmdname in known_commands:
            self.server.register_function(getattr(self.cmd_runner, cmdname), cmdname)

    def main(self, url):
        self.wallet.start_threads(self.network)
        while True:
            try:
                self.server.handle_request()
            except socket.timeout:
                continue
            except:
                break
        self.wallet.stop_threads()
예제 #3
0
 def load_wallet(self, path, password):
     # wizard will be launched if we return
     if path in self.wallets:
         wallet = self.wallets[path]
         return wallet
     storage = WalletStorage(path, manual_upgrades=True)
     if not storage.file_exists():
         return
     if storage.is_encrypted():
         if not password:
             return
         storage.decrypt(password)
     if storage.requires_split():
         return
     if storage.get_action():
         return
     wallet = Wallet(storage)
     wallet.start_threads(self.network)
     self.wallets[path] = wallet
     return wallet
예제 #4
0
class ElectrumGui:
    def __init__(self, config, network):
        self.network = network
        self.config = config
        storage = WalletStorage(config.get_wallet_path())
        if not storage.file_exists:
            print "Wallet not found. try 'electrum create'"
            exit()

        self.done = 0
        self.last_balance = ""

        set_verbosity(False)

        self.str_recipient = ""
        self.str_description = ""
        self.str_amount = ""
        self.str_fee = ""

        self.wallet = Wallet(storage)
        self.wallet.start_threads(network)
        self.contacts = StoreDict(self.config, 'contacts')

        self.wallet.network.register_callback('updated', self.updated)
        self.wallet.network.register_callback('connected', self.connected)
        self.wallet.network.register_callback('disconnected',
                                              self.disconnected)
        self.wallet.network.register_callback('disconnecting',
                                              self.disconnecting)
        self.wallet.network.register_callback('peers', self.peers)
        self.wallet.network.register_callback('banner', self.print_banner)
        self.commands = [_("[h] - displays this help text"), \
                         _("[i] - display transaction history"), \
                         _("[o] - enter payment order"), \
                         _("[p] - print stored payment order"), \
                         _("[s] - send stored payment order"), \
                         _("[r] - show own receipt addresses"), \
                         _("[c] - display contacts"), \
                         _("[b] - print server banner"), \
                         _("[q] - quit") ]
        self.num_commands = len(self.commands)

    def main_command(self):
        self.print_balance()
        c = raw_input("enter command: ")
        if c == "h": self.print_commands()
        elif c == "i": self.print_history()
        elif c == "o": self.enter_order()
        elif c == "p": self.print_order()
        elif c == "s": self.send_order()
        elif c == "r": self.print_addresses()
        elif c == "c": self.print_contacts()
        elif c == "b": self.print_banner()
        elif c == "n": self.network_dialog()
        elif c == "e": self.settings_dialog()
        elif c == "q": self.done = 1
        else: self.print_commands()

    def peers(self):
        print("got peers list:")
        l = filter_protocol(self.wallet.network.get_servers(), 's')
        for s in l:
            print(s)

    def connected(self):
        print("connected")

    def disconnected(self):
        print("disconnected")

    def disconnecting(self):
        print("disconnecting")

    def updated(self):
        s = self.get_balance()
        if s != self.last_balance:
            print(s)
        self.last_balance = s
        return True

    def print_commands(self):
        self.print_list(self.commands, "Available commands")

    def print_history(self):
        width = [20, 40, 14, 14]
        delta = (80 - sum(width) - 4) / 3
        format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%" \
        + "%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s"
        b = 0
        messages = []

        for item in self.wallet.get_history():
            tx_hash, confirmations, value, timestamp, balance = item
            if confirmations:
                try:
                    time_str = datetime.datetime.fromtimestamp(
                        timestamp).isoformat(' ')[:-3]
                except Exception:
                    time_str = "unknown"
            else:
                time_str = 'pending'

            label, is_default_label = self.wallet.get_label(tx_hash)
            messages.append(
                format_str %
                (time_str, label, format_satoshis(value, whitespaces=True),
                 format_satoshis(balance, whitespaces=True)))

        self.print_list(
            messages[::-1], format_str %
            (_("Date"), _("Description"), _("Amount"), _("Balance")))

    def print_balance(self):
        print(self.get_balance())

    def get_balance(self):
        if self.wallet.network.is_connected():
            if not self.wallet.up_to_date:
                msg = _("Synchronizing...")
            else:
                c, u, x = self.wallet.get_balance()
                msg = _("Balance") + ": %f  " % (Decimal(c) / COIN)
                if u:
                    msg += "  [%f unconfirmed]" % (Decimal(u) / COIN)
                if x:
                    msg += "  [%f unmatured]" % (Decimal(x) / COIN)
        else:
            msg = _("Not connected")

        return (msg)

    def print_contacts(self):
        messages = map(lambda x: "%20s   %45s " % (x[0], x[1][1]),
                       self.contacts.items())
        self.print_list(messages, "%19s  %25s " % ("Key", "Value"))

    def print_addresses(self):
        messages = map(
            lambda addr: "%30s    %30s       " %
            (addr, self.wallet.labels.get(addr, "")), self.wallet.addresses())
        self.print_list(messages, "%19s  %25s " % ("Address", "Label"))

    def print_order(self):
        print("send order to " + self.str_recipient + ", amount: " + self.str_amount \
              + "\nfee: " + self.str_fee + ", desc: " + self.str_description)

    def enter_order(self):
        self.str_recipient = raw_input("Pay to: ")
        self.str_description = raw_input("Description : ")
        self.str_amount = raw_input("Amount: ")
        self.str_fee = raw_input("Fee: ")

    def send_order(self):
        self.do_send()

    def print_banner(self):
        for i, x in enumerate(self.wallet.network.banner.split('\n')):
            print(x)

    def print_list(self, list, firstline):
        self.maxpos = len(list)
        if not self.maxpos: return
        print(firstline)
        for i in range(self.maxpos):
            msg = list[i] if i < len(list) else ""
            print(msg)

    def main(self, url):
        while self.done == 0:
            self.main_command()

    def do_send(self):
        if not is_valid(self.str_recipient):
            print(_('Invalid Unobtanium address'))
            return
        try:
            amount = int(Decimal(self.str_amount) * COIN)
        except Exception:
            print(_('Invalid Amount'))
            return
        try:
            fee = int(Decimal(self.str_fee) * COIN)
        except Exception:
            print(_('Invalid Fee'))
            return

        if self.wallet.use_encryption:
            password = self.password_dialog()
            if not password:
                return
        else:
            password = None

        c = ""
        while c != "y":
            c = raw_input("ok to send (y/n)?")
            if c == "n": return

        try:
            tx = self.wallet.mktx([(self.str_recipient, amount)], password,
                                  self.config, fee)
        except Exception as e:
            print(str(e))
            return

        if self.str_description:
            self.wallet.labels[tx.hash()] = self.str_description

        h = self.wallet.send_tx(tx)
        print(_("Please wait..."))
        self.wallet.tx_event.wait()
        status, msg = self.wallet.receive_tx(h, tx)

        if status:
            print(_('Payment sent.'))
            #self.do_clear()
            #self.update_contacts_tab()
        else:
            print(_('Error'))

    def network_dialog(self):
        print(
            "use 'electrum setconfig server/proxy' to change your network settings"
        )
        return True

    def settings_dialog(self):
        print("use 'electrum setconfig' to change your settings")
        return True

    def password_dialog(self):
        return getpass.getpass()

#   XXX unused

    def run_receive_tab(self, c):
        #if c == 10:
        #    out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"])
        return

    def run_contacts_tab(self, c):
        pass
예제 #5
0
파일: stdio.py 프로젝트: bontaq/electrum
class ElectrumGui:
    def __init__(self, config, network, daemon, plugins):
        self.network = network
        self.config = config
        storage = WalletStorage(config.get_wallet_path())
        if not storage.file_exists:
            print "Wallet not found. try 'electrum create'"
            exit()

        self.done = 0
        self.last_balance = ""

        set_verbosity(False)

        self.str_recipient = ""
        self.str_description = ""
        self.str_amount = ""
        self.str_fee = ""

        self.wallet = Wallet(storage)
        self.wallet.start_threads(network)
        self.contacts = StoreDict(self.config, "contacts")

        network.register_callback(self.on_network, ["updated", "banner"])
        self.commands = [
            _("[h] - displays this help text"),
            _("[i] - display transaction history"),
            _("[o] - enter payment order"),
            _("[p] - print stored payment order"),
            _("[s] - send stored payment order"),
            _("[r] - show own receipt addresses"),
            _("[c] - display contacts"),
            _("[b] - print server banner"),
            _("[q] - quit"),
        ]
        self.num_commands = len(self.commands)

    def on_network(self, event, *args):
        if event == "updated":
            self.updated()
        elif event == "banner":
            self.print_banner()

    def main_command(self):
        self.print_balance()
        c = raw_input("enter command: ")
        if c == "h":
            self.print_commands()
        elif c == "i":
            self.print_history()
        elif c == "o":
            self.enter_order()
        elif c == "p":
            self.print_order()
        elif c == "s":
            self.send_order()
        elif c == "r":
            self.print_addresses()
        elif c == "c":
            self.print_contacts()
        elif c == "b":
            self.print_banner()
        elif c == "n":
            self.network_dialog()
        elif c == "e":
            self.settings_dialog()
        elif c == "q":
            self.done = 1
        else:
            self.print_commands()

    def updated(self):
        s = self.get_balance()
        if s != self.last_balance:
            print (s)
        self.last_balance = s
        return True

    def print_commands(self):
        self.print_list(self.commands, "Available commands")

    def print_history(self):
        width = [20, 40, 14, 14]
        delta = (80 - sum(width) - 4) / 3
        format_str = (
            "%"
            + "%d" % width[0]
            + "s"
            + "%"
            + "%d" % (width[1] + delta)
            + "s"
            + "%"
            + "%d" % (width[2] + delta)
            + "s"
            + "%"
            + "%d" % (width[3] + delta)
            + "s"
        )
        b = 0
        messages = []

        for item in self.wallet.get_history():
            tx_hash, confirmations, value, timestamp, balance = item
            if confirmations:
                try:
                    time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(" ")[:-3]
                except Exception:
                    time_str = "unknown"
            else:
                time_str = "pending"

            label = self.wallet.get_label(tx_hash)
            messages.append(
                format_str
                % (
                    time_str,
                    label,
                    format_satoshis(value, whitespaces=True),
                    format_satoshis(balance, whitespaces=True),
                )
            )

        self.print_list(messages[::-1], format_str % (_("Date"), _("Description"), _("Amount"), _("Balance")))

    def print_balance(self):
        print (self.get_balance())

    def get_balance(self):
        if self.wallet.network.is_connected():
            if not self.wallet.up_to_date:
                msg = _("Synchronizing...")
            else:
                c, u, x = self.wallet.get_balance()
                msg = _("Balance") + ": %f  " % (Decimal(c) / COIN)
                if u:
                    msg += "  [%f unconfirmed]" % (Decimal(u) / COIN)
                if x:
                    msg += "  [%f unmatured]" % (Decimal(x) / COIN)
        else:
            msg = _("Not connected")

        return msg

    def print_contacts(self):
        messages = map(lambda x: "%20s   %45s " % (x[0], x[1][1]), self.contacts.items())
        self.print_list(messages, "%19s  %25s " % ("Key", "Value"))

    def print_addresses(self):
        messages = map(
            lambda addr: "%30s    %30s       " % (addr, self.wallet.labels.get(addr, "")), self.wallet.addresses()
        )
        self.print_list(messages, "%19s  %25s " % ("Address", "Label"))

    def print_order(self):
        print (
            "send order to "
            + self.str_recipient
            + ", amount: "
            + self.str_amount
            + "\nfee: "
            + self.str_fee
            + ", desc: "
            + self.str_description
        )

    def enter_order(self):
        self.str_recipient = raw_input("Pay to: ")
        self.str_description = raw_input("Description : ")
        self.str_amount = raw_input("Amount: ")
        self.str_fee = raw_input("Fee: ")

    def send_order(self):
        self.do_send()

    def print_banner(self):
        for i, x in enumerate(self.wallet.network.banner.split("\n")):
            print (x)

    def print_list(self, list, firstline):
        self.maxpos = len(list)
        if not self.maxpos:
            return
        print (firstline)
        for i in range(self.maxpos):
            msg = list[i] if i < len(list) else ""
            print (msg)

    def main(self):
        while self.done == 0:
            self.main_command()

    def do_send(self):
        if not is_valid(self.str_recipient):
            print (_("Invalid Bitcoin address"))
            return
        try:
            amount = int(Decimal(self.str_amount) * COIN)
        except Exception:
            print (_("Invalid Amount"))
            return
        try:
            fee = int(Decimal(self.str_fee) * COIN)
        except Exception:
            print (_("Invalid Fee"))
            return

        if self.wallet.use_encryption:
            password = self.password_dialog()
            if not password:
                return
        else:
            password = None

        c = ""
        while c != "y":
            c = raw_input("ok to send (y/n)?")
            if c == "n":
                return

        try:
            tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)], password, self.config, fee)
        except Exception as e:
            print (str(e))
            return

        if self.str_description:
            self.wallet.labels[tx.hash()] = self.str_description

        h = self.wallet.send_tx(tx)
        print (_("Please wait..."))
        self.wallet.tx_event.wait()
        status, msg = self.wallet.receive_tx(h, tx)

        if status:
            print (_("Payment sent."))
            # self.do_clear()
            # self.update_contacts_tab()
        else:
            print (_("Error"))

    def network_dialog(self):
        print ("use 'electrum setconfig server/proxy' to change your network settings")
        return True

    def settings_dialog(self):
        print ("use 'electrum setconfig' to change your settings")
        return True

    def password_dialog(self):
        return getpass.getpass()

    #   XXX unused

    def run_receive_tab(self, c):
        # if c == 10:
        #    out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"])
        return

    def run_contacts_tab(self, c):
        pass
예제 #6
0
class ElectrumWallet:
    """
    An Electrum wallet wrapper
    """
    def __init__(self, name, config):
        """
        Initializes new electrum wallet instance

        @param name: Name of the wallet
        @param config: Configuration dictionary e.g {
            'server': 'localhost:7777:s',
            'rpc_user': '******',
            'rpc_pass_': 'pass',
            'electrum_path': '/opt/var/data/electrum',
            'seed': '....',
            'fee': 10000,
            'testnet': 1
        }
        """
        self._name = name
        self._config = config
        self._config['testnet'] = bool(self._config['testnet'])
        if self._config['testnet'] is True:
            constants.set_testnet()

        self._config['verbos'] = False
        self._electrum_config = SimpleConfig(self._config)
        self._wallet_path = os.path.join(self._electrum_config.path, 'wallets',
                                         self._name)
        self._storage = WalletStorage(path=self._wallet_path)
        if not self._storage.file_exists():
            self._electrum_config.set_key('default_wallet_path',
                                          self._wallet_path)
            k = keystore.from_seed(self._config['seed'],
                                   self._config['passphrase'], False)
            k.update_password(None, self._config['password'])
            self._storage.put('keystore', k.dump())
            self._storage.put('wallet_type', 'standard')
            self._storage.put('use_encryption', bool(self._config['password']))
            self._storage.write()
            self._wallet = Wallet(self._storage)
            # self._server = daemon.get_server(self._electrum_config)
            self._network = Network(self._electrum_config)
            self._network.start()
            self._wallet.start_threads(self._network)
            self._wallet.synchronize()
            self._wallet.wait_until_synchronized()
            self._wallet.stop_threads()
            self._wallet.storage.write()
        else:
            self._network = None
            self._wallet = self._wallet = Wallet(self._storage)

        self._commands = Commands(config=self._electrum_config,
                                  wallet=self._wallet,
                                  network=self._network)

        self._init_commands()

    def _init_commands(self):
        """
        Scans the electrum commands class and binds all its methods to this class
        """
        execlude_cmd = lambda item: (not item[0].startswith('_')) and item[
            0] not in EXECLUDED_COMMANDS
        for name, func in filter(
                execlude_cmd,
                inspect.getmembers(self._commands, inspect.ismethod)):
            setattr(self, name, func)
예제 #7
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    def __init__(self, config, app, plugins, network, storage):

        BaseWizard.__init__(self, config, network, storage)
        QDialog.__init__(self, None)

        self.setWindowTitle('Electrum  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config

        # Set for base base class
        self.plugins = plugins
        self.language_for_seed = config.get('language')
        self.setMinimumSize(530, 370)
        self.setMaximumSize(530, 370)
        self.connect(self, QtCore.SIGNAL('accept'), self.accept)
        self.title = QLabel()
        self.main_widget = QWidget()
        self.back_button = QPushButton(_("Back"), self)
        self.next_button = QPushButton(_("Next"), self)
        self.next_button.setDefault(True)
        self.logo = QLabel()
        self.please_wait = QLabel(_("Please wait..."))
        self.please_wait.setAlignment(Qt.AlignCenter)
        self.icon_filename = None
        self.loop = QEventLoop()
        self.rejected.connect(lambda: self.loop.exit(0))
        self.back_button.clicked.connect(lambda: self.loop.exit(1))
        self.next_button.clicked.connect(lambda: self.loop.exit(2))
        outer_vbox = QVBoxLayout(self)
        inner_vbox = QVBoxLayout()
        inner_vbox = QVBoxLayout()
        inner_vbox.addWidget(self.title)
        inner_vbox.addWidget(self.main_widget)
        inner_vbox.addStretch(1)
        inner_vbox.addWidget(self.please_wait)
        inner_vbox.addStretch(1)
        icon_vbox = QVBoxLayout()
        icon_vbox.addWidget(self.logo)
        icon_vbox.addStretch(1)
        hbox = QHBoxLayout()
        hbox.addLayout(icon_vbox)
        hbox.addSpacing(5)
        hbox.addLayout(inner_vbox)
        hbox.setStretchFactor(inner_vbox, 1)
        outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.set_icon(':icons/electrum.png')
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.

    def run_and_get_wallet(self):
        # Show network dialog if config does not exist
        if self.network:
            if self.config.get('auto_connect') is None:
                self.choose_server(self.network)

        path = self.storage.path
        if self.storage.requires_split():
            self.hide()
            msg = _("The wallet '%s' contains multiple accounts, which are no longer supported in Electrum 2.7.\n\n"
                    "Do you want to split your wallet into multiple files?"%path)
            if not self.question(msg):
                return
            file_list = '\n'.join(self.storage.split_accounts())
            msg = _('Your accounts have been moved to:\n %s.\n\nDo you want to delete the old file:\n%s' % (file_list, path))
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            return

        if self.storage.requires_upgrade():
            self.hide()
            msg = _("The format of your wallet '%s' must be upgraded for Electrum. This change will not be backward compatible"%path)
            if not self.question(msg):
                return
            self.storage.upgrade()
            self.show_warning(_('Your wallet was upgraded successfully'))
            self.wallet = Wallet(self.storage)
            self.terminate()
            return self.wallet

        action = self.storage.get_action()
        if action and action != 'new':
            self.hide()
            msg = _("The file '%s' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?") % path
            if not self.question(msg):
                if self.question(_("Do you want to delete '%s'?") % path):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
        if action:
            # self.wallet is set in run
            self.run(action)
            return self.wallet


    def finished(self):
        """Called in hardware client wrapper, in order to close popups."""
        return

    def on_error(self, exc_info):
        if not isinstance(exc_info[1], UserCancelled):
            traceback.print_exception(*exc_info)
            self.show_error(str(exc_info[1]))

    def set_icon(self, filename):
        prior_filename, self.icon_filename = self.icon_filename, filename
        self.logo.setPixmap(QPixmap(filename).scaledToWidth(60))
        return prior_filename

    def set_main_layout(self, layout, title=None, raise_on_cancel=True,
                        next_enabled=True):
        self.title.setText("<b>%s</b>"%title if title else "")
        self.title.setVisible(bool(title))
        # Get rid of any prior layout by assigning it to a temporary widget
        prior_layout = self.main_widget.layout()
        if prior_layout:
            QWidget().setLayout(prior_layout)
        self.main_widget.setLayout(layout)
        self.back_button.setEnabled(True)
        self.next_button.setEnabled(next_enabled)
        self.main_widget.setVisible(True)
        self.please_wait.setVisible(False)
        result = self.loop.exec_()
        if not result and raise_on_cancel:
            raise UserCancelled
        if result == 1:
            raise GoBack
        self.title.setVisible(False)
        self.back_button.setEnabled(False)
        self.next_button.setEnabled(False)
        self.main_widget.setVisible(False)
        self.please_wait.setVisible(True)
        self.refresh_gui()
        return result

    def refresh_gui(self):
        # For some reason, to refresh the GUI this needs to be called twice
        self.app.processEvents()
        self.app.processEvents()

    def remove_from_recently_open(self, filename):
        self.config.remove_from_recently_open(filename)

    def text_input(self, title, message, is_valid):
        slayout = SeedInputLayout(title=message)
        def sanitized_seed():
            return clean_text(slayout.seed_edit())
        def set_enabled():
            self.next_button.setEnabled(is_valid(sanitized_seed()))
        slayout.seed_edit().textChanged.connect(set_enabled)
        self.set_main_layout(slayout.layout(), title, next_enabled=False)
        seed = sanitized_seed()
        return seed

    @wizard_dialog
    def restore_keys_dialog(self, title, message, is_valid, run_next):
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def add_cosigner_dialog(self, run_next, index, is_valid):
        title = _("Add Cosigner") + " %d"%index
        message = ' '.join([
            _('Please enter the master public key of your cosigner.'),
            _('Enter their seed or master private key if you want to be able to sign for them.')
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def restore_seed_dialog(self, run_next, is_valid):
        title = _('Enter Seed')
        message = _('Please enter your seed phrase in order to restore your wallet.')
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, is_valid):
        self.app.clipboard().clear()
        title = _('Confirm Seed')
        message = ' '.join([
            _('Your seed is important!'),
            _('To make sure that you have properly saved your seed, please retype it here.')
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def show_seed_dialog(self, run_next, seed_text):
        slayout = SeedWarningLayout(seed_text)
        self.set_main_layout(slayout.layout())
        return seed_text

    def pw_layout(self, msg, kind):
        playout = PasswordLayout(None, msg, kind, self.next_button)
        self.set_main_layout(playout.layout())
        return playout.new_password()

    @wizard_dialog
    def request_passphrase(self, device_text, run_next):
        """When restoring a wallet, request the passphrase that was used for
        the wallet on the given device and confirm it.  Should return
        a unicode string."""
        phrase = self.pw_layout(MSG_RESTORE_PASSPHRASE % device_text,
                                PW_PASSPHRASE)
        if phrase is None:
            raise UserCancelled
        return phrase

    @wizard_dialog
    def request_password(self, run_next):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW)

    def show_restore(self, wallet, network):
        # FIXME: these messages are shown after the install wizard is
        # finished and the window closed.  On MacOSX they appear parented
        # with a re-appeared ghost install wizard window...
        if network:
            def task():
                wallet.wait_until_synchronized()
                if wallet.is_found():
                    msg = _("Recovery successful")
                else:
                    msg = _("No transactions found for this seed")
                self.emit(QtCore.SIGNAL('synchronized'), msg)
            self.connect(self, QtCore.SIGNAL('synchronized'), self.show_message)
            t = threading.Thread(target = task)
            t.daemon = True
            t.start()
        else:
            msg = _("This wallet was restored offline. It may "
                    "contain more addresses than displayed.")
            self.show_message(msg)

    @wizard_dialog
    def confirm_dialog(self, title, message, run_next):
        self.confirm(message, title)

    def confirm(self, message, title):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        self.set_main_layout(vbox, title)

    @wizard_dialog
    def action_dialog(self, action, run_next):
        self.run(action)

    def terminate(self):
        self.wallet.start_threads(self.network)
        self.emit(QtCore.SIGNAL('accept'))

    def waiting_dialog(self, task, msg):
        self.please_wait.setText(MSG_GENERATING_WAIT)
        self.refresh_gui()
        t = threading.Thread(target = task)
        t.start()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next):
        c_values = map(lambda x: x[0], choices)
        c_titles = map(lambda x: x[1], choices)
        clayout = ChoicesLayout(message, c_titles)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.set_main_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        return action

    def query_choice(self, msg, choices):
        """called by hardware wallets"""
        clayout = ChoicesLayout(msg, choices)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.set_main_layout(vbox, '')
        return clayout.selected_index()

    def get_passphrase(self, msg, confirm):
        phrase = self.pw_layout(msg, PW_PASSPHRASE)
        if phrase is None:
            raise UserCancelled
        return phrase


    @wizard_dialog
    def account_id_dialog(self, run_next):
        message = '\n'.join([
            _('Enter your account number here.'),
            _('If you are not sure what this is, leave this field to zero.')
        ])
        default = '0'
        title = _('Account Number')
        line = QLineEdit()
        line.setText(default)
        vbox = QVBoxLayout()
        vbox.addWidget(QLabel(message))
        vbox.addWidget(line)
        self.set_main_layout(vbox, title)
        return int(line.text())

    @wizard_dialog
    def show_xpub_dialog(self, xpub, run_next):
        msg = ' '.join([
            _("Here is your master public key."),
            _("Please share it with your cosigners.")
        ])
        vbox = QVBoxLayout()
        layout = SeedDisplayLayout(xpub, title=msg, sid='hot')
        vbox.addLayout(layout.layout())
        self.set_main_layout(vbox, _('Master Public Key'))
        return None

    def choose_server(self, network):
        title = _("Electrum communicates with remote servers to get "
                  "information about your transactions and addresses. The "
                  "servers all fulfil the same purpose only differing in "
                  "hardware. In most cases you simply want to let Electrum "
                  "pick one at random.  However if you prefer feel free to "
                  "select a server manually.")
        choices = [_("Auto connect"), _("Select server manually")]
        choices_title = _("How do you want to connect to a server? ")
        clayout = ChoicesLayout(choices_title, choices)
        self.set_main_layout(clayout.layout(), title)
        auto_connect = True
        if clayout.selected_index() == 1:
            nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
            if self.set_main_layout(nlayout.layout(), raise_on_cancel=False):
                nlayout.accept()
                auto_connect = False
        self.config.set_key('auto_connect', auto_connect, True)
        network.auto_connect = auto_connect

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QSlider(Qt.Horizontal, self)
        n_edit = QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(2)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        n_edit.setValue(2)
        m_edit.setValue(2)
        n_label = QLabel()
        m_label = QLabel()
        grid = QGridLayout()
        grid.addWidget(n_label, 0, 0)
        grid.addWidget(n_edit, 0, 1)
        grid.addWidget(m_label, 1, 0)
        grid.addWidget(m_edit, 1, 1)
        def on_m(m):
            m_label.setText(_('Require %d signatures')%m)
            cw.set_m(m)
        def on_n(n):
            n_label.setText(_('From %d cosigners')%n)
            cw.set_n(n)
            m_edit.setMaximum(n)
        n_edit.valueChanged.connect(on_n)
        m_edit.valueChanged.connect(on_m)
        on_n(2)
        on_m(2)
        vbox = QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(WWLabel(_("Choose the number of signatures needed to unlock funds in your wallet:")))
        vbox.addLayout(grid)
        self.set_main_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)
예제 #8
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    def __init__(self, config, app, plugins, network, storage):

        BaseWizard.__init__(self, config, network, storage)
        QDialog.__init__(self, None)

        self.setWindowTitle('Electrum  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config

        # Set for base base class
        self.plugins = plugins
        self.language_for_seed = config.get('language')
        self.setMinimumSize(530, 370)
        self.setMaximumSize(530, 370)
        self.connect(self, QtCore.SIGNAL('accept'), self.accept)
        self.title = QLabel()
        self.main_widget = QWidget()
        self.back_button = QPushButton(_("Back"), self)
        self.next_button = QPushButton(_("Next"), self)
        self.next_button.setDefault(True)
        self.logo = QLabel()
        self.please_wait = QLabel(_("Please wait..."))
        self.please_wait.setAlignment(Qt.AlignCenter)
        self.icon_filename = None
        self.loop = QEventLoop()
        self.rejected.connect(lambda: self.loop.exit(0))
        self.back_button.clicked.connect(lambda: self.loop.exit(1))
        self.next_button.clicked.connect(lambda: self.loop.exit(2))
        outer_vbox = QVBoxLayout(self)
        inner_vbox = QVBoxLayout()
        inner_vbox = QVBoxLayout()
        inner_vbox.addWidget(self.title)
        inner_vbox.addWidget(self.main_widget)
        inner_vbox.addStretch(1)
        inner_vbox.addWidget(self.please_wait)
        inner_vbox.addStretch(1)
        icon_vbox = QVBoxLayout()
        icon_vbox.addWidget(self.logo)
        icon_vbox.addStretch(1)
        hbox = QHBoxLayout()
        hbox.addLayout(icon_vbox)
        hbox.addSpacing(5)
        hbox.addLayout(inner_vbox)
        hbox.setStretchFactor(inner_vbox, 1)
        outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.set_icon(':icons/electrum.png')
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.

    def run_and_get_wallet(self):
        # Show network dialog if config does not exist
        if self.network:
            if self.config.get('auto_connect') is None:
                self.choose_server(self.network)

        path = self.storage.path
        if self.storage.requires_split():
            self.hide()
            msg = _("The wallet '%s' contains multiple accounts, which are no longer supported in Electrum 2.7.\n\n"
                    "Do you want to split your wallet into multiple files?"%path)
            if not self.question(msg):
                return
            file_list = '\n'.join(self.storage.split_accounts())
            msg = _('Your accounts have been moved to:\n %s.\n\nDo you want to delete the old file:\n%s' % (file_list, path))
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            return

        if self.storage.requires_upgrade():
            self.hide()
            msg = _("The format of your wallet '%s' must be upgraded for Electrum. This change will not be backward compatible"%path)
            if not self.question(msg):
                return
            self.storage.upgrade()
            self.show_warning(_('Your wallet was upgraded successfully'))
            self.wallet = Wallet(self.storage)
            self.terminate()
            return self.wallet

        action = self.storage.get_action()
        if action and action != 'new':
            self.hide()
            msg = _("The file '%s' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?") % path
            if not self.question(msg):
                if self.question(_("Do you want to delete '%s'?") % path):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
        if action:
            # self.wallet is set in run
            self.run(action)
            return self.wallet


    def finished(self):
        '''Ensure the dialog is closed.'''
        self.accept()
        self.refresh_gui()

    def on_error(self, exc_info):
        if not isinstance(exc_info[1], UserCancelled):
            traceback.print_exception(*exc_info)
            self.show_error(str(exc_info[1]))

    def set_icon(self, filename):
        prior_filename, self.icon_filename = self.icon_filename, filename
        self.logo.setPixmap(QPixmap(filename).scaledToWidth(60))
        return prior_filename

    def set_main_layout(self, layout, title=None, raise_on_cancel=True,
                        next_enabled=True):
        self.title.setText("<b>%s</b>"%title if title else "")
        self.title.setVisible(bool(title))
        # Get rid of any prior layout by assigning it to a temporary widget
        prior_layout = self.main_widget.layout()
        if prior_layout:
            QWidget().setLayout(prior_layout)
        self.main_widget.setLayout(layout)
        self.back_button.setEnabled(True)
        self.next_button.setEnabled(next_enabled)
        self.main_widget.setVisible(True)
        self.please_wait.setVisible(False)
        result = self.loop.exec_()
        if not result and raise_on_cancel:
            raise UserCancelled
        if result == 1:
            raise GoBack
        self.title.setVisible(False)
        self.back_button.setEnabled(False)
        self.next_button.setEnabled(False)
        self.main_widget.setVisible(False)
        self.please_wait.setVisible(True)
        self.refresh_gui()
        return result

    def refresh_gui(self):
        # For some reason, to refresh the GUI this needs to be called twice
        self.app.processEvents()
        self.app.processEvents()

    def remove_from_recently_open(self, filename):
        self.config.remove_from_recently_open(filename)

    def text_input(self, title, message, is_valid):
        slayout = SeedInputLayout(title=message)
        def sanitized_seed():
            return clean_text(slayout.seed_edit())
        def set_enabled():
            self.next_button.setEnabled(is_valid(sanitized_seed()))
        slayout.seed_edit().textChanged.connect(set_enabled)
        self.set_main_layout(slayout.layout(), title, next_enabled=False)
        seed = sanitized_seed()
        return seed

    @wizard_dialog
    def restore_keys_dialog(self, title, message, is_valid, run_next):
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def add_cosigner_dialog(self, run_next, index, is_valid):
        title = _("Add Cosigner") + " %d"%index
        message = ' '.join([
            _('Please enter the master public key of your cosigner.'),
            _('Enter their seed or master private key if you want to be able to sign for them.')
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def restore_seed_dialog(self, run_next, is_valid):
        title = _('Enter Seed')
        message = _('Please enter your seed phrase in order to restore your wallet.')
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, is_valid):
        self.app.clipboard().clear()
        title = _('Confirm Seed')
        message = ' '.join([
            _('Your seed is important!'),
            _('To make sure that you have properly saved your seed, please retype it here.')
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def show_seed_dialog(self, run_next, seed_text):
        slayout = SeedWarningLayout(seed_text)
        self.set_main_layout(slayout.layout())
        return seed_text

    def pw_layout(self, msg, kind):
        playout = PasswordLayout(None, msg, kind, self.next_button)
        self.set_main_layout(playout.layout())
        return playout.new_password()

    @wizard_dialog
    def request_passphrase(self, device_text, run_next):
        """When restoring a wallet, request the passphrase that was used for
        the wallet on the given device and confirm it.  Should return
        a unicode string."""
        phrase = self.pw_layout(MSG_RESTORE_PASSPHRASE % device_text,
                                PW_PASSPHRASE)
        if phrase is None:
            raise UserCancelled
        return phrase

    @wizard_dialog
    def request_password(self, run_next):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW)

    def show_restore(self, wallet, network):
        # FIXME: these messages are shown after the install wizard is
        # finished and the window closed.  On MacOSX they appear parented
        # with a re-appeared ghost install wizard window...
        if network:
            def task():
                wallet.wait_until_synchronized()
                if wallet.is_found():
                    msg = _("Recovery successful")
                else:
                    msg = _("No transactions found for this seed")
                self.emit(QtCore.SIGNAL('synchronized'), msg)
            self.connect(self, QtCore.SIGNAL('synchronized'), self.show_message)
            t = threading.Thread(target = task)
            t.daemon = True
            t.start()
        else:
            msg = _("This wallet was restored offline. It may "
                    "contain more addresses than displayed.")
            self.show_message(msg)

    @wizard_dialog
    def confirm_dialog(self, msg, run_next):
        self.confirm(msg)

    def confirm(self, msg):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(msg))
        self.set_main_layout(vbox)

    @wizard_dialog
    def action_dialog(self, action, run_next):
        self.run(action)

    def terminate(self):
        self.wallet.start_threads(self.network)
        self.emit(QtCore.SIGNAL('accept'))

    def waiting_dialog(self, task, msg):
        self.please_wait.setText(MSG_GENERATING_WAIT)
        self.refresh_gui()
        t = threading.Thread(target = task)
        t.start()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next):
        c_values = map(lambda x: x[0], choices)
        c_titles = map(lambda x: x[1], choices)
        clayout = ChoicesLayout(message, c_titles)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.set_main_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        return action

    @wizard_dialog
    def show_xpub_dialog(self, xpub, run_next):
        msg = ' '.join([
            _("Here is your master public key."),
            _("Please share it with your cosigners.")
        ])
        vbox = QVBoxLayout()
        layout = SeedDisplayLayout(xpub, title=msg, sid='hot')
        vbox.addLayout(layout.layout())
        self.set_main_layout(vbox, _('Master Public Key'))
        return None

    def choose_server(self, network):
        title = _("Electrum communicates with remote servers to get "
                  "information about your transactions and addresses. The "
                  "servers all fulfil the same purpose only differing in "
                  "hardware. In most cases you simply want to let Electrum "
                  "pick one at random.  However if you prefer feel free to "
                  "select a server manually.")
        choices = [_("Auto connect"), _("Select server manually")]
        choices_title = _("How do you want to connect to a server? ")
        clayout = ChoicesLayout(choices_title, choices)
        self.set_main_layout(clayout.layout(), title)
        auto_connect = True
        if clayout.selected_index() == 1:
            nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
            if self.set_main_layout(nlayout.layout(), raise_on_cancel=False):
                nlayout.accept()
                auto_connect = False
        self.config.set_key('auto_connect', auto_connect, True)
        network.auto_connect = auto_connect

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QSlider(Qt.Horizontal, self)
        n_edit = QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(2)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        n_edit.setValue(2)
        m_edit.setValue(2)
        n_label = QLabel()
        m_label = QLabel()
        grid = QGridLayout()
        grid.addWidget(n_label, 0, 0)
        grid.addWidget(n_edit, 0, 1)
        grid.addWidget(m_label, 1, 0)
        grid.addWidget(m_edit, 1, 1)
        def on_m(m):
            m_label.setText(_('Require %d signatures')%m)
            cw.set_m(m)
        def on_n(n):
            n_label.setText(_('From %d cosigners')%n)
            cw.set_n(n)
            m_edit.setMaximum(n)
        n_edit.valueChanged.connect(on_n)
        m_edit.valueChanged.connect(on_m)
        on_n(2)
        on_m(2)
        vbox = QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(WWLabel(_("Choose the number of signatures needed to unlock funds in your wallet:")))
        vbox.addLayout(grid)
        self.set_main_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)