commit 7f305730270d63d64c2cefb87942aeb42cf60388
parent 0524e5ddd1e072beb276c6baaec45dff487f4c19
Author: ThomasV <thomasv@gitorious>
Date: Tue, 14 Feb 2012 09:52:03 +0100
wallet recovery: use static methods
Diffstat:
4 files changed, 227 insertions(+), 101 deletions(-)
diff --git a/client/electrum b/client/electrum
@@ -60,16 +60,23 @@ if __name__ == '__main__':
import gui
interface.get_servers()
+ gui = gui.ElectrumGui(wallet)
+
try:
found = wallet.read()
if not found:
- gui.restore_create_dialog(wallet)
+ found = gui.restore_or_create()
except BaseException, e:
- gui.show_message(e.message)
+ import traceback
+ traceback.print_exc(file=sys.stdout)
+ #gui.show_message(e.message)
exit(1)
-
- gui = gui.BitcoinGUI(wallet)
+
+ if not found: exit(1)
+
interface.start(wallet)
+ gui.main()
+ sys.exit(0)
if re.match('^bitcoin:', cmd):
diff --git a/client/gui.py b/client/gui.py
@@ -88,8 +88,8 @@ def restore_create_dialog(wallet):
dialog.show()
r = dialog.run()
dialog.destroy()
- if r==2:
- sys.exit(1)
+
+ if r==2: return False
is_recovery = (r==1)
@@ -108,7 +108,6 @@ def restore_create_dialog(wallet):
#ask for password
change_password_dialog(wallet, None, None)
-
else:
# ask for the server.
run_network_dialog( wallet, parent=None )
@@ -132,7 +131,6 @@ def restore_create_dialog(wallet):
wallet.update_tx_history()
wallet.fill_addressbook()
print "recovery successful"
- wallet.save()
gobject.idle_add( dialog.destroy )
@@ -143,6 +141,9 @@ def restore_create_dialog(wallet):
if not wallet.is_found:
show_message("No transactions found for this seed")
+ wallet.save()
+ return True
+
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."
@@ -215,7 +216,7 @@ def run_recovery_dialog(wallet):
def run_settings_dialog(wallet, parent):
- message = "These are the settings of your wallet. For more explanations, click on the question mark buttons next to each input field."
+ message = "Here are the settings of your wallet. For more explanations, click on the question mark buttons next to each input field."
dialog = gtk.MessageDialog(
parent = parent,
@@ -461,7 +462,7 @@ gtk.binding_entry_add_signal(MyWindow, gtk.keysyms.W, gtk.gdk.CONTROL_MASK, 'myk
gtk.binding_entry_add_signal(MyWindow, gtk.keysyms.Q, gtk.gdk.CONTROL_MASK, 'mykeypress', str, 'ctrl+Q')
-class BitcoinGUI:
+class ElectrumWindow:
def show_message(self, msg):
show_message(msg, self.window)
@@ -567,7 +568,7 @@ class BitcoinGUI:
r = r.strip()
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
try:
- to_address = self.get_alias(r, interactive=False)
+ to_address = self.wallet.get_alias(r, interactive=False)
except:
continue
if to_address:
@@ -699,7 +700,7 @@ class BitcoinGUI:
if signature:
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity):
- signing_address = self.get_alias(identity, interactive = True)
+ signing_address = self.wallet.get_alias(identity, True, self.show_message, self.question)
elif self.wallet.is_valid(identity):
signing_address = identity
else:
@@ -717,7 +718,7 @@ class BitcoinGUI:
#if label and payto:
# self.labels[payto] = label
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', payto):
- payto_address = self.get_alias(payto, interactive=True)
+ payto_address = self.wallet.get_alias(payto, True, self.show_message, self.question)
if payto_address:
payto = payto + ' <' + payto_address + '>'
@@ -757,46 +758,6 @@ class BitcoinGUI:
dialog.destroy()
return result == gtk.RESPONSE_OK
- def get_alias(self, alias, interactive = False):
- try:
- target, signing_address, auth_name = self.wallet.read_alias(alias)
- except BaseException, e:
- # raise exception if verify fails (verify the chain)
- if interactive:
- self.show_message("Alias error: " + e.message)
- return
-
- print target, signing_address, auth_name
-
- if auth_name is None:
- a = self.wallet.aliases.get(alias)
- if not a:
- msg = "Warning: the alias '%s' is self-signed.\nThe signing address is %s.\n\nDo you want to add this alias to your list of contacts?"%(alias,signing_address)
- if interactive and self.question( msg ):
- self.wallet.aliases[alias] = (signing_address, target)
- else:
- target = None
- else:
- if signing_address != a[0]:
- msg = "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias
- if interactive and self.question( msg ):
- self.wallet.aliases[alias] = (signing_address, target)
- else:
- target = None
- else:
- if signing_address not in self.wallet.authorities.keys():
- msg = "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"%(alias,target,auth_name,signing_address)
- if interactive and self.question( msg ):
- self.wallet.authorities[signing_address] = auth_name
- else:
- target = None
-
- if target:
- self.wallet.aliases[alias] = (signing_address, target)
- self.update_sending_tab()
-
-
- return target
@@ -810,9 +771,12 @@ class BitcoinGUI:
m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
if m1:
- to_address = self.get_alias(r, interactive = True)
+ to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
if not to_address:
return
+ else:
+ self.update_sending_tab()
+
elif m2:
to_address = m2.group(5)
else:
@@ -1258,6 +1222,15 @@ class BitcoinGUI:
errorDialog.destroy()
+
+class ElectrumGui():
+
+ def __init__(self, wallet):
+ self.wallet = wallet
+
def main(self):
+ ElectrumWindow(self.wallet)
gtk.main()
+ def restore_or_create(self):
+ return restore_create_dialog(self.wallet)
diff --git a/client/gui_qt.py b/client/gui_qt.py
@@ -10,8 +10,10 @@ import PyQt4.QtGui as QtGui
from wallet import format_satoshis
from decimal import Decimal
-def restore_create_dialog(wallet):
- pass
+
+
+
+
class Sender(QtCore.QThread):
def run(self):
@@ -33,6 +35,18 @@ class StatusBarButton(QPushButton):
apply(self.func,())
+def ok_cancel_buttons(dialog):
+ hbox = QHBoxLayout()
+ hbox.addStretch(1)
+ b = QPushButton("OK")
+ hbox.addWidget(b)
+ b.clicked.connect(dialog.accept)
+ b = QPushButton("Cancel")
+ hbox.addWidget(b)
+ b.clicked.connect(dialog.reject)
+ return hbox
+
+
class ElectrumWindow(QMainWindow):
def __init__(self, wallet):
@@ -58,16 +72,7 @@ class ElectrumWindow(QMainWindow):
QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
- def ok_cancel_buttons(self,d):
- hbox = QHBoxLayout()
- hbox.addStretch(1)
- b = QPushButton("OK")
- hbox.addWidget(b)
- b.clicked.connect(d.accept)
- b = QPushButton("Cancel")
- hbox.addWidget(b)
- b.clicked.connect(d.reject)
- return hbox
+
def connect_slots(self, sender):
self.connect(sender, QtCore.SIGNAL('testsignal'), self.update_wallet)
@@ -272,7 +277,7 @@ class ElectrumWindow(QMainWindow):
m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
if m1:
- to_address = self.get_alias(r, interactive = True)
+ to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
if not to_address:
return
elif m2:
@@ -439,10 +444,10 @@ class ElectrumWindow(QMainWindow):
def create_status_bar(self):
sb = QStatusBar()
sb.setFixedHeight(35)
- sb.addPermanentWidget( StatusBarButton( QIcon("icons/lock.svg"), "Password", self.change_password_dialog ) )
+ sb.addPermanentWidget( StatusBarButton( QIcon("icons/lock.svg"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
sb.addPermanentWidget( StatusBarButton( QIcon("icons/preferences.png"), "Preferences", self.settings_dialog ) )
- sb.addPermanentWidget( StatusBarButton( QIcon("icons/seed.png"), "Seed", self.show_seed_dialog ) )
- self.status_button = StatusBarButton( QIcon("icons/status_disconnected.png"), "Network", self.network_dialog )
+ sb.addPermanentWidget( StatusBarButton( QIcon("icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
+ self.status_button = StatusBarButton( QIcon("icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
sb.addPermanentWidget( self.status_button )
self.setStatusBar(sb)
@@ -458,18 +463,19 @@ class ElectrumWindow(QMainWindow):
else:
QMessageBox.warning(self, 'Error', 'Invalid Address', 'OK')
- def show_seed_dialog(self):
+ @staticmethod
+ def show_seed_dialog(wallet, parent=None):
import mnemonic
- if self.wallet.use_encryption:
+ if wallet.use_encryption:
password = self.password_dialog()
if not password: return
else:
password = None
try:
- seed = self.wallet.pw_decode( self.wallet.seed, password)
+ seed = wallet.pw_decode( wallet.seed, password)
except:
- QMessageBox.warning(self, 'Error', 'Invalid Password', 'OK')
+ QMessageBox.warning(parent, 'Error', 'Invalid Password', 'OK')
return
msg = "Your wallet generation seed is:\n\n" + seed \
@@ -477,11 +483,17 @@ class ElectrumWindow(QMainWindow):
+ "Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:\n\n\"" \
+ ' '.join(mnemonic.mn_encode(seed)) + "\""
- QMessageBox.information(self, 'Seed', msg, 'OK')
+ QMessageBox.information(parent, 'Seed', msg, 'OK')
+ def question(self, msg):
+ return QMessageBox.question(self, 'Message', msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
- def password_dialog(self):
- d = QDialog(self)
+ def show_message(self, msg):
+ QMessageBox.information(self, 'Message', msg, 'OK')
+
+ @staticmethod
+ def password_dialog( parent=None ):
+ d = QDialog(parent)
d.setModal(1)
pw = QLineEdit()
@@ -497,14 +509,15 @@ class ElectrumWindow(QMainWindow):
grid.addWidget(pw, 1, 1)
vbox.addLayout(grid)
- vbox.addLayout(self.ok_cancel_buttons(d))
+ vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
if not d.exec_(): return
return str(pw.text())
- def change_password_dialog(self):
- d = QDialog(self)
+ @staticmethod
+ def change_password_dialog( wallet, parent=None ):
+ d = QDialog(parent)
d.setModal(1)
pw = QLineEdit()
@@ -515,13 +528,13 @@ class ElectrumWindow(QMainWindow):
conf_pw.setEchoMode(2)
vbox = QVBoxLayout()
- msg = 'Your wallet is encrypted. Use this dialog to change your password.\nTo disable wallet encryption, enter an empty new password.' if self.wallet.use_encryption else 'Your wallet keys are not encrypted'
+ msg = 'Your wallet is encrypted. Use this dialog to change your password.\nTo disable wallet encryption, enter an empty new password.' if wallet.use_encryption else 'Your wallet keys are not encrypted'
vbox.addWidget(QLabel(msg))
grid = QGridLayout()
grid.setSpacing(8)
- if self.wallet.use_encryption:
+ if wallet.use_encryption:
grid.addWidget(QLabel('Password'), 1, 0)
grid.addWidget(pw, 1, 1)
@@ -532,26 +545,76 @@ class ElectrumWindow(QMainWindow):
grid.addWidget(conf_pw, 3, 1)
vbox.addLayout(grid)
- vbox.addLayout(self.ok_cancel_buttons(d))
+ vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
if not d.exec_(): return
- password = str(pw.text()) if self.wallet.use_encryption else None
+ password = str(pw.text()) if wallet.use_encryption else None
new_password = str(new_pw.text())
new_password2 = str(conf_pw.text())
try:
- seed = self.wallet.pw_decode( self.wallet.seed, password)
+ seed = wallet.pw_decode( wallet.seed, password)
except:
- QMessageBox.warning(self, 'Error', 'Incorrect Password', 'OK')
+ QMessageBox.warning(parent, 'Error', 'Incorrect Password', 'OK')
return
if new_password != new_password2:
- QMessageBox.warning(self, 'Error', 'Passwords do not match', 'OK')
+ QMessageBox.warning(parent, 'Error', 'Passwords do not match', 'OK')
return
- self.wallet.update_password(seed, new_password)
+ wallet.update_password(seed, new_password)
+
+ @staticmethod
+ def seed_dialog(wallet, parent=None):
+ d = QDialog(parent)
+ d.setModal(1)
+
+ vbox = QVBoxLayout()
+ msg = "Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet."
+ vbox.addWidget(QLabel(msg))
+
+ grid = QGridLayout()
+ grid.setSpacing(8)
+
+ seed_e = QLineEdit()
+ grid.addWidget(QLabel('Seed or mnemonic'), 1, 0)
+ grid.addWidget(seed_e, 1, 1)
+
+ gap_e = QLineEdit()
+ gap_e.setText("5")
+ grid.addWidget(QLabel('Gap limit'), 2, 0)
+ grid.addWidget(gap_e, 2, 1)
+
+ vbox.addLayout(grid)
+
+ vbox.addLayout(ok_cancel_buttons(d))
+ d.setLayout(vbox)
+
+ if not d.exec_(): return
+
+ try:
+ gap = int(str(gap_e.text()))
+ except:
+ show_message("error")
+ sys.exit(1)
+
+ try:
+ seed = str(seed_e.text())
+ seed.decode('hex')
+ except:
+ import mnemonic
+ print "not hex, trying decode"
+ seed = mnemonic.mn_decode( seed.split(' ') )
+ if not seed:
+ show_message("no seed")
+ sys.exit(1)
+
+ wallet.seed = seed
+ wallet.gap_limit = gap
+ return True
+
def settings_dialog(self):
d = QDialog(self)
@@ -559,7 +622,7 @@ class ElectrumWindow(QMainWindow):
vbox = QVBoxLayout()
- msg = 'These are the settings of your wallet'
+ msg = 'Here are the settings of your wallet'
vbox.addWidget(QLabel(msg))
grid = QGridLayout()
@@ -571,7 +634,7 @@ class ElectrumWindow(QMainWindow):
grid.addWidget(fee_line, 2, 1)
vbox.addLayout(grid)
- vbox.addLayout(self.ok_cancel_buttons(d))
+ vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
if not d.exec_(): return
@@ -586,8 +649,9 @@ class ElectrumWindow(QMainWindow):
self.wallet.fee = fee
self.wallet.save()
- def network_dialog(self):
- wallet = self.wallet
+ @staticmethod
+ def network_dialog(wallet, parent=None):
+
if True:
if wallet.interface.is_connected:
status = "Connected to %s.\n%d blocks\nresponse time: %f"%(wallet.interface.host, wallet.interface.blocks, wallet.interface.rtime)
@@ -601,7 +665,7 @@ class ElectrumWindow(QMainWindow):
host = random.choice( wallet.interface.servers )
port = 50000
- d = QDialog(self)
+ d = QDialog(parent)
d.setModal(1)
vbox = QVBoxLayout()
@@ -615,7 +679,7 @@ class ElectrumWindow(QMainWindow):
grid.addWidget(host_line, 2, 1)
vbox.addLayout(grid)
- vbox.addLayout(self.ok_cancel_buttons(d))
+ vbox.addLayout(ok_cancel_buttons(d))
d.setLayout(vbox)
if not d.exec_(): return
@@ -636,23 +700,65 @@ class ElectrumWindow(QMainWindow):
return
wallet.interface.set_server(host, port)
+ return True
- if parent:
- wallet.save()
-
-class BitcoinGUI():
+class ElectrumGui():
def __init__(self, wallet):
self.wallet = wallet
+ self.app = QApplication(sys.argv)
+
+ def restore_or_create(self):
+
+ msg = "Wallet file not found.\nDo you want to create a new wallet,\n or to restore an existing one?"
+ r = QMessageBox.question(None, 'Message', msg, 'create', 'restore', 'cancel', 0, 2)
+ if r==2: return False
+
+ is_recovery = (r==1)
+ wallet = self.wallet
+ if not is_recovery:
+ wallet.new_seed(None)
+ # ask for the server.
+ ElectrumWindow.network_dialog(wallet)
+ # generate first key
+ wallet.synchronize()
+ # run a dialog indicating the seed, ask the user to remember it
+ ElectrumWindow.show_seed_dialog(wallet)
+ #ask for password
+ ElectrumWindow.change_password_dialog(wallet)
+ else:
+ # ask for the server.
+ r = ElectrumWindow.network_dialog( wallet, parent=None )
+ if not r: return False
+ # ask for seed and gap.
+ r = ElectrumWindow.seed_dialog( wallet )
+ if not r: return False
+
+ wallet.init_mpk( wallet.seed ) # not encrypted at this point
+ wallet.synchronize()
+
+ if wallet.is_found():
+ # history and addressbook
+ wallet.update_tx_history()
+ wallet.fill_addressbook()
+ print "recovery successful"
+ wallet.save()
+ else:
+ QMessageBox.information(None, 'Message', "No transactions found for this seed", 'OK')
+
+ wallet.save()
+ return True
+
+
def main(self):
+
s = Sender()
s.start()
- app = QApplication(sys.argv)
w = ElectrumWindow(self.wallet)
- w.app = app
+ w.app = self.app
w.connect_slots(s)
- app.exec_()
+ self.app.exec_()
diff --git a/client/wallet.py b/client/wallet.py
@@ -769,3 +769,43 @@ class Wallet:
c = self.pw_encode(b, new_password)
self.imported_keys[k] = c
self.save()
+
+
+ def get_alias(self, alias, interactive = False, show_message=None, question = None):
+ try:
+ target, signing_address, auth_name = self.read_alias(alias)
+ except BaseException, e:
+ # raise exception if verify fails (verify the chain)
+ if interactive:
+ show_message("Alias error: " + e.message)
+ return
+
+ print target, signing_address, auth_name
+
+ if auth_name is None:
+ a = self.aliases.get(alias)
+ if not a:
+ msg = "Warning: the alias '%s' is self-signed.\nThe signing address is %s.\n\nDo you want to add this alias to your list of contacts?"%(alias,signing_address)
+ if interactive and question( msg ):
+ self.aliases[alias] = (signing_address, target)
+ else:
+ target = None
+ else:
+ if signing_address != a[0]:
+ msg = "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias
+ if interactive and question( msg ):
+ self.aliases[alias] = (signing_address, target)
+ else:
+ target = None
+ else:
+ if signing_address not in self.authorities.keys():
+ msg = "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"%(alias,target,auth_name,signing_address)
+ if interactive and question( msg ):
+ self.authorities[signing_address] = auth_name
+ else:
+ target = None
+
+ if target:
+ self.aliases[alias] = (signing_address, target)
+
+ return target