Some work on multisig.
This commit is contained in:
parent
576500aa29
commit
0219687d41
|
@ -7,7 +7,7 @@ import PyQt4.QtCore as QtCore
|
||||||
import electrum
|
import electrum
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
|
|
||||||
import seed_dialog
|
from seed_dialog import SeedDisplayLayout, SeedWarningLayout, SeedInputLayout
|
||||||
from network_dialog import NetworkDialog
|
from network_dialog import NetworkDialog
|
||||||
from util import *
|
from util import *
|
||||||
from password_dialog import PasswordLayout, PW_NEW, PW_PASSPHRASE
|
from password_dialog import PasswordLayout, PW_NEW, PW_PASSPHRASE
|
||||||
|
@ -32,6 +32,8 @@ class CosignWidget(QWidget):
|
||||||
QWidget.__init__(self)
|
QWidget.__init__(self)
|
||||||
self.R = QRect(0, 0, self.size, self.size)
|
self.R = QRect(0, 0, self.size, self.size)
|
||||||
self.setGeometry(self.R)
|
self.setGeometry(self.R)
|
||||||
|
self.setMinimumHeight(self.size)
|
||||||
|
self.setMaximumHeight(self.size)
|
||||||
self.m = m
|
self.m = m
|
||||||
self.n = n
|
self.n = n
|
||||||
|
|
||||||
|
@ -74,8 +76,7 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
self.setMinimumSize(518, 360)
|
self.setMinimumSize(518, 360)
|
||||||
self.setMaximumSize(518, 360)
|
self.setMaximumSize(518, 360)
|
||||||
self.connect(self, QtCore.SIGNAL('accept'), self.accept)
|
self.connect(self, QtCore.SIGNAL('accept'), self.accept)
|
||||||
self.title = QLabel()
|
self.title = WWLabel()
|
||||||
self.title.setWordWrap(True)
|
|
||||||
self.main_widget = QWidget()
|
self.main_widget = QWidget()
|
||||||
self.cancel_button = QPushButton(_("Cancel"), self)
|
self.cancel_button = QPushButton(_("Cancel"), self)
|
||||||
self.next_button = QPushButton(_("Next"), self)
|
self.next_button = QPushButton(_("Next"), self)
|
||||||
|
@ -100,13 +101,12 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
icon_vbox.addStretch(1)
|
icon_vbox.addStretch(1)
|
||||||
hbox = QHBoxLayout()
|
hbox = QHBoxLayout()
|
||||||
hbox.addLayout(icon_vbox)
|
hbox.addLayout(icon_vbox)
|
||||||
|
hbox.addSpacing(5)
|
||||||
hbox.addLayout(inner_vbox)
|
hbox.addLayout(inner_vbox)
|
||||||
hbox.setStretchFactor(inner_vbox, 1)
|
hbox.setStretchFactor(inner_vbox, 1)
|
||||||
outer_vbox.addLayout(hbox)
|
outer_vbox.addLayout(hbox)
|
||||||
outer_vbox.addLayout(Buttons(self.cancel_button, self.next_button))
|
outer_vbox.addLayout(Buttons(self.cancel_button, self.next_button))
|
||||||
self.set_icon(':icons/electrum.png')
|
self.set_icon(':icons/electrum.png')
|
||||||
self.show()
|
|
||||||
self.raise_()
|
|
||||||
|
|
||||||
def set_icon(self, filename):
|
def set_icon(self, filename):
|
||||||
prior_filename, self.icon_filename = self.icon_filename, filename
|
prior_filename, self.icon_filename = self.icon_filename, filename
|
||||||
|
@ -120,6 +120,9 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
prior_layout = self.main_widget.layout()
|
prior_layout = self.main_widget.layout()
|
||||||
if prior_layout:
|
if prior_layout:
|
||||||
QWidget().setLayout(prior_layout)
|
QWidget().setLayout(prior_layout)
|
||||||
|
else:
|
||||||
|
self.show()
|
||||||
|
self.raise_()
|
||||||
self.main_widget.setLayout(layout)
|
self.main_widget.setLayout(layout)
|
||||||
self.cancel_button.setEnabled(True)
|
self.cancel_button.setEnabled(True)
|
||||||
self.next_button.setEnabled(True)
|
self.next_button.setEnabled(True)
|
||||||
|
@ -155,7 +158,7 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
|
|
||||||
def request_seed(self, title, is_valid=None):
|
def request_seed(self, title, is_valid=None):
|
||||||
is_valid = is_valid or Wallet.is_any
|
is_valid = is_valid or Wallet.is_any
|
||||||
slayout = seed_dialog.SeedLayout(None)
|
slayout = SeedInputLayout()
|
||||||
self.next_button.setEnabled(False)
|
self.next_button.setEnabled(False)
|
||||||
def sanitized_seed():
|
def sanitized_seed():
|
||||||
return clean_text(slayout.seed_edit())
|
return clean_text(slayout.seed_edit())
|
||||||
|
@ -167,7 +170,7 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
|
|
||||||
def show_seed(self, seed):
|
def show_seed(self, seed):
|
||||||
title = _("Your wallet generation seed is:")
|
title = _("Your wallet generation seed is:")
|
||||||
slayout = seed_dialog.SeedLayout(seed)
|
slayout = SeedWarningLayout(seed)
|
||||||
self.set_main_layout(slayout.layout(), title)
|
self.set_main_layout(slayout.layout(), title)
|
||||||
|
|
||||||
def verify_seed(self, seed, is_valid=None):
|
def verify_seed(self, seed, is_valid=None):
|
||||||
|
@ -185,11 +188,8 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
self.verify_seed(seed, is_valid)
|
self.verify_seed(seed, is_valid)
|
||||||
|
|
||||||
def pw_layout(self, msg, kind):
|
def pw_layout(self, msg, kind):
|
||||||
hbox = QHBoxLayout()
|
|
||||||
playout = PasswordLayout(None, msg, kind, self.next_button)
|
playout = PasswordLayout(None, msg, kind, self.next_button)
|
||||||
hbox.addLayout(playout.layout())
|
self.set_main_layout(playout.layout())
|
||||||
#hbox.addStretch(1)
|
|
||||||
self.set_main_layout(hbox)
|
|
||||||
return playout.new_password()
|
return playout.new_password()
|
||||||
|
|
||||||
def request_passphrase(self, device_text, restore=True):
|
def request_passphrase(self, device_text, restore=True):
|
||||||
|
@ -234,13 +234,6 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
self.please_wait.setText(MSG_GENERATING_WAIT)
|
self.please_wait.setText(MSG_GENERATING_WAIT)
|
||||||
self.refresh_gui()
|
self.refresh_gui()
|
||||||
|
|
||||||
def set_layout(self, layout):
|
|
||||||
w = QWidget()
|
|
||||||
w.setLayout(layout)
|
|
||||||
self.stack.addWidget(w)
|
|
||||||
self.stack.setCurrentWidget(w)
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def query_create_or_restore(self, wallet_kinds):
|
def query_create_or_restore(self, wallet_kinds):
|
||||||
"""Ask the user what they want to do, and which wallet kind.
|
"""Ask the user what they want to do, and which wallet kind.
|
||||||
wallet_kinds is an array of translated wallet descriptions.
|
wallet_kinds is an array of translated wallet descriptions.
|
||||||
|
@ -264,37 +257,30 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
def request_many(self, n, xpub_hot=None):
|
def request_many(self, n, xpub_hot=None):
|
||||||
vbox = QVBoxLayout()
|
vbox = QVBoxLayout()
|
||||||
scroll = QScrollArea()
|
scroll = QScrollArea()
|
||||||
scroll.setEnabled(True)
|
|
||||||
scroll.setWidgetResizable(True)
|
scroll.setWidgetResizable(True)
|
||||||
|
scroll.setFrameShape(QFrame.NoFrame)
|
||||||
vbox.addWidget(scroll)
|
vbox.addWidget(scroll)
|
||||||
|
|
||||||
w = QWidget()
|
w = QWidget()
|
||||||
|
innerVbox = QVBoxLayout(w)
|
||||||
scroll.setWidget(w)
|
scroll.setWidget(w)
|
||||||
|
|
||||||
innerVbox = QVBoxLayout()
|
|
||||||
w.setLayout(innerVbox)
|
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
if xpub_hot:
|
if xpub_hot:
|
||||||
vbox0 = seed_dialog.show_seed_box(MSG_SHOW_MPK, xpub_hot, 'hot')
|
layout = SeedDisplayLayout(xpub_hot, title=MSG_SHOW_MPK, sid='hot')
|
||||||
else:
|
else:
|
||||||
vbox0, seed_e1 = seed_dialog.enter_seed_box(MSG_ENTER_SEED_OR_MPK, self, 'hot')
|
layout = SeedInputLayout(title=MSG_ENTER_SEED_OR_MPK, sid='hot')
|
||||||
entries.append(seed_e1)
|
entries.append(slayout.seed_edit())
|
||||||
innerVbox.addLayout(vbox0)
|
innerVbox.addLayout(layout.layout())
|
||||||
|
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
if xpub_hot:
|
msg = MSG_COSIGNER % (i + 1) if xpub_hot else MSG_ENTER_SEED_OR_MPK
|
||||||
msg = MSG_COSIGNER % (i + 1)
|
layout = SeedInputLayout(title=msg, sid='cold')
|
||||||
else:
|
innerVbox.addLayout(layout.layout())
|
||||||
msg = MSG_ENTER_SEED_OR_MPK
|
entries.append(layout.seed_edit())
|
||||||
vbox2, seed_e2 = seed_dialog.enter_seed_box(msg, self, 'cold')
|
|
||||||
innerVbox.addLayout(vbox2)
|
|
||||||
entries.append(seed_e2)
|
|
||||||
|
|
||||||
vbox.addStretch(1)
|
self.next_button.setEnabled(False)
|
||||||
button = OkButton(self, _('Next'))
|
|
||||||
vbox.addLayout(Buttons(CancelButton(self), button))
|
|
||||||
button.setEnabled(False)
|
|
||||||
def get_texts():
|
def get_texts():
|
||||||
return [clean_text(entry) for entry in entries]
|
return [clean_text(entry) for entry in entries]
|
||||||
def set_enabled():
|
def set_enabled():
|
||||||
|
@ -304,12 +290,10 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
if xpub_hot:
|
if xpub_hot:
|
||||||
texts.append(xpub_hot)
|
texts.append(xpub_hot)
|
||||||
has_dups = len(set(texts)) < len(texts)
|
has_dups = len(set(texts)) < len(texts)
|
||||||
button.setEnabled(all_valid and not has_dups)
|
self.next_button.setEnabled(all_valid and not has_dups)
|
||||||
for e in entries:
|
for e in entries:
|
||||||
e.textChanged.connect(set_enabled)
|
e.textChanged.connect(set_enabled)
|
||||||
self.set_layout(vbox)
|
self.set_main_layout(vbox)
|
||||||
if not self.exec_():
|
|
||||||
raise UserCancelled
|
|
||||||
return get_texts()
|
return get_texts()
|
||||||
|
|
||||||
def network_dialog(self, network):
|
def network_dialog(self, network):
|
||||||
|
@ -355,14 +339,7 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
return clayout.selected_index()
|
return clayout.selected_index()
|
||||||
|
|
||||||
def query_multisig(self, action):
|
def query_multisig(self, action):
|
||||||
vbox = QVBoxLayout()
|
|
||||||
self.set_layout(vbox)
|
|
||||||
vbox.addWidget(QLabel(_("Multi Signature Wallet")))
|
|
||||||
|
|
||||||
cw = CosignWidget(2, 2)
|
cw = CosignWidget(2, 2)
|
||||||
vbox.addWidget(cw, 1)
|
|
||||||
vbox.addWidget(QLabel(_("Please choose the number of signatures needed to unlock funds in your wallet") + ':'))
|
|
||||||
|
|
||||||
m_edit = QSpinBox()
|
m_edit = QSpinBox()
|
||||||
n_edit = QSpinBox()
|
n_edit = QSpinBox()
|
||||||
m_edit.setValue(2)
|
m_edit.setValue(2)
|
||||||
|
@ -384,11 +361,12 @@ class InstallWizard(WindowModalDialog, WizardBase):
|
||||||
hbox.addWidget(QLabel(_('signatures')))
|
hbox.addWidget(QLabel(_('signatures')))
|
||||||
hbox.addStretch(1)
|
hbox.addStretch(1)
|
||||||
|
|
||||||
|
vbox = QVBoxLayout()
|
||||||
|
vbox.addWidget(cw)
|
||||||
|
vbox.addWidget(WWLabel(_("Choose the number of signatures needed "
|
||||||
|
"to unlock funds in your wallet:")))
|
||||||
vbox.addLayout(hbox)
|
vbox.addLayout(hbox)
|
||||||
vbox.addStretch(1)
|
self.set_main_layout(vbox, _("Multi-Signature Wallet"))
|
||||||
vbox.addLayout(Buttons(CancelButton(self), OkButton(self, _('Next'))))
|
|
||||||
if not self.exec_():
|
|
||||||
raise UserCancelled
|
|
||||||
m = int(m_edit.value())
|
m = int(m_edit.value())
|
||||||
n = int(n_edit.value())
|
n = int(n_edit.value())
|
||||||
wallet_type = '%dof%d'%(m,n)
|
wallet_type = '%dof%d'%(m,n)
|
||||||
|
|
|
@ -18,23 +18,11 @@
|
||||||
|
|
||||||
from PyQt4.QtGui import *
|
from PyQt4.QtGui import *
|
||||||
from PyQt4.QtCore import *
|
from PyQt4.QtCore import *
|
||||||
import PyQt4.QtCore as QtCore
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
|
|
||||||
from util import *
|
from util import *
|
||||||
from qrtextedit import ShowQRTextEdit, ScanQRTextEdit
|
from qrtextedit import ShowQRTextEdit, ScanQRTextEdit
|
||||||
|
|
||||||
class SeedDialog(WindowModalDialog):
|
|
||||||
def __init__(self, parent, seed, imported_keys):
|
|
||||||
WindowModalDialog.__init__(self, parent, ('Electrum - ' + _('Seed')))
|
|
||||||
self.setMinimumWidth(400)
|
|
||||||
vbox = show_seed_box_msg(seed)
|
|
||||||
if imported_keys:
|
|
||||||
vbox.addWidget(QLabel("<b>"+_("WARNING")+":</b> " + _("Your wallet contains imported keys. These keys cannot be recovered from seed.") + "</b><p>"))
|
|
||||||
vbox.addLayout(Buttons(CloseButton(self)))
|
|
||||||
self.setLayout(vbox)
|
|
||||||
|
|
||||||
|
|
||||||
def icon_filename(sid):
|
def icon_filename(sid):
|
||||||
if sid == 'cold':
|
if sid == 'cold':
|
||||||
return ":icons/cold_seed.png"
|
return ":icons/cold_seed.png"
|
||||||
|
@ -43,22 +31,61 @@ def icon_filename(sid):
|
||||||
else:
|
else:
|
||||||
return ":icons/seed.png"
|
return ":icons/seed.png"
|
||||||
|
|
||||||
class SeedLayout(object):
|
class SeedDialog(WindowModalDialog):
|
||||||
def __init__(self, seed, sid=None):
|
def __init__(self, parent, seed, imported_keys):
|
||||||
|
WindowModalDialog.__init__(self, parent, ('Electrum - ' + _('Seed')))
|
||||||
|
self.setMinimumWidth(400)
|
||||||
|
vbox = QVBoxLayout(self)
|
||||||
|
vbox.addLayout(SeedDisplayLayout(seed))
|
||||||
|
if imported_keys:
|
||||||
|
warning = ("<b>" + _("WARNING") + ":</b> " +
|
||||||
|
_("Your wallet contains imported keys. These keys "
|
||||||
|
"cannot be recovered from your seed.") + "</b><p>")
|
||||||
|
vbox.addWidget(WWLabel(warning))
|
||||||
|
vbox.addLayout(Buttons(CloseButton(self)))
|
||||||
|
|
||||||
|
|
||||||
|
class SeedLayoutBase(object):
|
||||||
|
def _seed_layout(self, seed=None, title=None, sid=None):
|
||||||
|
logo = QLabel()
|
||||||
|
logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56))
|
||||||
|
logo.setMaximumWidth(60)
|
||||||
if seed:
|
if seed:
|
||||||
self.vbox = self.seed_and_warning_layout(seed, sid)
|
self.seed_e = ShowQRTextEdit()
|
||||||
|
self.seed_e.setText(seed)
|
||||||
else:
|
else:
|
||||||
self.vbox = self.seed_layout(seed, sid)
|
self.seed_e = ScanQRTextEdit()
|
||||||
|
self.seed_e.setTabChangesFocus(True)
|
||||||
|
self.seed_e.setMaximumHeight(75)
|
||||||
|
hbox = QHBoxLayout()
|
||||||
|
hbox.addWidget(logo)
|
||||||
|
hbox.addWidget(self.seed_e)
|
||||||
|
if not title:
|
||||||
|
return hbox
|
||||||
|
vbox = QVBoxLayout()
|
||||||
|
vbox.addWidget(WWLabel(title))
|
||||||
|
vbox.addLayout(hbox)
|
||||||
|
return vbox
|
||||||
|
|
||||||
def layout(self):
|
def layout(self):
|
||||||
return self.vbox
|
return self.layout_
|
||||||
|
|
||||||
def seed_edit(self):
|
def seed_edit(self):
|
||||||
return self.seed_e
|
return self.seed_e
|
||||||
|
|
||||||
def seed_and_warning_layout(self, seed, sid=None):
|
|
||||||
vbox = QVBoxLayout()
|
class SeedInputLayout(SeedLayoutBase):
|
||||||
vbox.addLayout(self.seed_layout(seed, sid))
|
def __init__(self, title=None, sid=None):
|
||||||
|
self.layout_ = self._seed_layout(title=title, sid=sid)
|
||||||
|
|
||||||
|
|
||||||
|
class SeedDisplayLayout(SeedLayoutBase):
|
||||||
|
def __init__(self, seed, title=None, sid=None):
|
||||||
|
self.layout_ = self._seed_layout(seed=seed, title=title, sid=sid)
|
||||||
|
|
||||||
|
|
||||||
|
class SeedWarningLayout(SeedLayoutBase):
|
||||||
|
def __init__(self, seed, title=None):
|
||||||
msg = ''.join([
|
msg = ''.join([
|
||||||
"<p>",
|
"<p>",
|
||||||
_("Please save these %d words on paper (order is important). "),
|
_("Please save these %d words on paper (order is important). "),
|
||||||
|
@ -72,23 +99,7 @@ class SeedLayout(object):
|
||||||
"<li>" + _("Do not send your seed to a printer.") + "</li>",
|
"<li>" + _("Do not send your seed to a printer.") + "</li>",
|
||||||
"</ul>"
|
"</ul>"
|
||||||
]) % len(seed.split())
|
]) % len(seed.split())
|
||||||
label2 = QLabel(msg)
|
vbox = QVBoxLayout()
|
||||||
label2.setWordWrap(True)
|
vbox.addLayout(self._seed_layout(seed=seed, title=title))
|
||||||
vbox.addWidget(label2)
|
vbox.addWidget(WWLabel(msg))
|
||||||
return vbox
|
self.layout_ = vbox
|
||||||
|
|
||||||
def seed_layout(self, seed, sid=None):
|
|
||||||
logo = QLabel()
|
|
||||||
logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56))
|
|
||||||
logo.setMaximumWidth(60)
|
|
||||||
if not seed:
|
|
||||||
seed_e = ScanQRTextEdit()
|
|
||||||
seed_e.setTabChangesFocus(True)
|
|
||||||
else:
|
|
||||||
seed_e = ShowQRTextEdit(text=seed)
|
|
||||||
seed_e.setMaximumHeight(100)
|
|
||||||
self.seed_e = seed_e
|
|
||||||
hbox = QHBoxLayout()
|
|
||||||
hbox.addWidget(logo)
|
|
||||||
hbox.addWidget(seed_e)
|
|
||||||
return hbox
|
|
||||||
|
|
|
@ -86,6 +86,12 @@ class ThreadedButton(QPushButton):
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
|
|
||||||
|
class WWLabel(QLabel):
|
||||||
|
def __init__ (self, text="", parent=None):
|
||||||
|
QLabel.__init__(self, text, parent)
|
||||||
|
self.setWordWrap(True)
|
||||||
|
|
||||||
|
|
||||||
class HelpLabel(QLabel):
|
class HelpLabel(QLabel):
|
||||||
|
|
||||||
def __init__(self, text, help_text):
|
def __init__(self, text, help_text):
|
||||||
|
@ -272,9 +278,7 @@ class ChoicesLayout(object):
|
||||||
def __init__(self, msg, choices, on_clicked=None):
|
def __init__(self, msg, choices, on_clicked=None):
|
||||||
vbox = QVBoxLayout()
|
vbox = QVBoxLayout()
|
||||||
if len(msg) > 50:
|
if len(msg) > 50:
|
||||||
label = QLabel(msg)
|
vbox.addWidget(WWLabel(msg))
|
||||||
label.setWordWrap(True)
|
|
||||||
vbox.addWidget(label)
|
|
||||||
msg = ""
|
msg = ""
|
||||||
gb2 = QGroupBox(msg)
|
gb2 = QGroupBox(msg)
|
||||||
vbox.addWidget(gb2)
|
vbox.addWidget(gb2)
|
||||||
|
|
|
@ -108,7 +108,7 @@ class Plugin(TrustedCoinPlugin):
|
||||||
label.setWordWrap(True)
|
label.setWordWrap(True)
|
||||||
vbox = QVBoxLayout()
|
vbox = QVBoxLayout()
|
||||||
vbox.addWidget(label)
|
vbox.addWidget(label)
|
||||||
window.set_main_layout(vbox, _("Two-Factor Authentication"))
|
window.set_main_layout(vbox)
|
||||||
self.set_enabled(wallet, True)
|
self.set_enabled(wallet, True)
|
||||||
window.set_icon(prior_icon)
|
window.set_icon(prior_icon)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue