From 6c96b38abf34e5bf20f744dbad6bf9dcdde4e15b Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 19 Apr 2014 20:23:27 +0200 Subject: [PATCH] installwizard: multisig wallets --- gui/qt/installwizard.py | 185 ++++++++++++++++++++++------------------------- gui/qt/seed_dialog.py | 48 +++++++++++- gui/qt/util.py | 6 ++- icons.qrc | 2 + icons/cold_seed.png | Bin 0 -> 9883 bytes icons/hot_seed.png | Bin 0 -> 9993 bytes lib/wallet.py | 27 ++++++- 7 files changed, 162 insertions(+), 106 deletions(-) create mode 100644 icons/cold_seed.png create mode 100644 icons/hot_seed.png diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py index f203b1b..b0180ca 100644 --- a/gui/qt/installwizard.py +++ b/gui/qt/installwizard.py @@ -5,7 +5,7 @@ import PyQt4.QtCore as QtCore from electrum.i18n import _ from electrum import Wallet, Wallet_2of3 -from seed_dialog import SeedDialog +import seed_dialog from network_dialog import NetworkDialog from util import * from amountedit import AmountEdit @@ -53,14 +53,10 @@ class InstallWizard(QDialog): b1.setChecked(True) b2 = QRadioButton(gb) - b2.setText(_("Restore an existing wallet from its seed")) - - b3 = QRadioButton(gb) - b3.setText(_("Create a watching-only version of an existing wallet")) + b2.setText(_("Restore an existing wallet")) grid.addWidget(b1,1,0) grid.addWidget(b2,2,0) - grid.addWidget(b3,3,0) vbox = QVBoxLayout() self.set_layout(vbox) @@ -72,21 +68,12 @@ class InstallWizard(QDialog): if not self.exec_(): return - if b1.isChecked(): - answer = 'create' - elif b2.isChecked(): - answer = 'restore' - else: - answer = 'watching' + return 'create' if b1.isChecked() else 'restore' - return answer - - - - def verify_seed(self, seed): - r = self.seed_dialog(False) + def verify_seed(self, seed, sid): + r = self.enter_seed_dialog(False, sid) if not r: return @@ -97,52 +84,46 @@ class InstallWizard(QDialog): return True - def seed_dialog(self, is_restore=True): + def get_seed_text(self, seed_e): + return unicode(seed_e.toPlainText()) - vbox = QVBoxLayout() - if is_restore: - msg = _("Please enter your wallet seed.") + "\n" - else: - msg = _("Your seed is important!") \ - + "\n" + _("To make sure that you have properly saved your seed, please retype it here.") - - logo = QLabel() - logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56)) - logo.setMaximumWidth(60) - label = QLabel(msg) - label.setWordWrap(True) + def is_seed(self, seed_e): + text = self.get_seed_text(seed_e) + return Wallet.is_seed(text) or Wallet.is_mpk(text) - seed_e = QTextEdit() - seed_e.setMaximumHeight(100) - - vbox.addWidget(label) - - grid = QGridLayout() - grid.addWidget(logo, 0, 0) - grid.addWidget(seed_e, 0, 1) - - vbox.addLayout(grid) + def enter_seed_dialog(self, is_restore, sid): + vbox, seed_e = seed_dialog.enter_seed_box(is_restore, sid) vbox.addStretch(1) - vbox.addLayout(ok_cancel_buttons(self, _('Next'))) - + hbox, button = ok_cancel_buttons2(self, _('Next')) + vbox.addLayout(hbox) + button.setEnabled(False) + seed_e.textChanged.connect(lambda: button.setEnabled(self.is_seed(seed_e))) self.set_layout(vbox) if not self.exec_(): return + return self.get_seed_text(seed_e) - seed = seed_e.toPlainText() - seed = unicode(seed.toLower()) - if not seed: - QMessageBox.warning(None, _('Error'), _('No seed'), _('OK')) - return - - if not Wallet.is_seed(seed): - QMessageBox.warning(None, _('Error'), _('Invalid seed'), _('OK')) - return + def double_seed_dialog(self): + vbox = QVBoxLayout() + vbox1, seed_e1 = seed_dialog.enter_seed_box(True, 'hot') + vbox2, seed_e2 = seed_dialog.enter_seed_box(True, 'cold') + vbox.addLayout(vbox1) + vbox.addLayout(vbox2) + vbox.addStretch(1) + hbox, button = ok_cancel_buttons2(self, _('Next')) + vbox.addLayout(hbox) + button.setEnabled(False) + f = lambda: button.setEnabled(self.is_seed(seed_e1) and self.is_seed(seed_e2)) + seed_e1.textChanged.connect(f) + seed_e2.textChanged.connect(f) + self.set_layout(vbox) + if not self.exec_(): + return + return self.get_seed_text(seed_e1), self.get_seed_text(seed_e2) - return seed @@ -161,32 +142,6 @@ class InstallWizard(QDialog): - def mpk_dialog(self): - - vbox = QVBoxLayout() - vbox.addWidget(QLabel(_("Please enter your master public key."))) - - grid = QGridLayout() - grid.setSpacing(8) - - label = QLabel(_("Key")) - grid.addWidget(label, 0, 0) - mpk_e = QTextEdit() - mpk_e.setMaximumHeight(100) - grid.addWidget(mpk_e, 0, 1) - - vbox.addLayout(grid) - - vbox.addStretch(1) - vbox.addLayout(ok_cancel_buttons(self, _('Next'))) - - self.set_layout(vbox) - if not self.exec_(): - return None - - mpk = str(mpk_e.toPlainText()).strip() - return mpk - def network_dialog(self): @@ -258,8 +213,7 @@ class InstallWizard(QDialog): def show_seed(self, seed, sid): - from seed_dialog import make_seed_dialog - vbox = make_seed_dialog(seed, sid) + vbox = seed_dialog.show_seed_box(seed, sid) vbox.addLayout(ok_cancel_buttons(self, _("Next"))) self.set_layout(vbox) return self.exec_() @@ -320,6 +274,9 @@ class InstallWizard(QDialog): if action == 'create': t = self.choose_wallet_type() + if not t: + return + if t == '2of3': run_hook('create_cold_seed', self.storage, self) return @@ -331,35 +288,67 @@ class InstallWizard(QDialog): wallet.init_seed(None) seed = wallet.get_mnemonic(None) - if not self.show_seed(seed, 'hot' if action == 'create2of3' else None): + sid = 'hot' if action == 'create2of3' else None + if not self.show_seed(seed, sid): return - if not self.verify_seed(seed): + if not self.verify_seed(seed, sid): return ok, old_password, password = self.password_dialog(wallet) wallet.save_seed(password) if action == 'create2of3': - run_hook('create_hot_seed', wallet, self) + run_hook('create_third_key', wallet, self) + if not wallet.master_public_keys.get("remote/"): + return wallet.create_accounts(password) - def create(): - wallet.synchronize() # generate first addresses offline - self.waiting_dialog(create) + # generate first addresses offline + self.waiting_dialog(wallet.synchronize) elif action == 'restore': - seed = self.seed_dialog() - if not Wallet.is_seed(seed): + # dialog box will accept either seed or xpub. + # use two boxes for 2of3 + t = self.choose_wallet_type() + if not t: return - wallet = Wallet.from_seed(seed, self.storage) - ok, old_password, password = self.password_dialog(wallet) - wallet.save_seed(password) - wallet.create_accounts(password) - elif action == 'watching': - mpk = self.mpk_dialog() - if not mpk: - return - wallet = Wallet.from_mpk(mpk, self.storage) + if t == 'standard': + text = self.enter_seed_dialog(True, None) + if Wallet.is_seed(text): + wallet = Wallet.from_seed(text, self.storage) + ok, old_password, password = self.password_dialog(wallet) + wallet.save_seed(password) + wallet.create_accounts(password) + elif Wallet.is_mpk(text): + wallet = Wallet.from_mpk(text, self.storage) + else: + return + + elif t in ['2of2', '2of3']: + r = self.double_seed_dialog() + if not r: + return + text1, text2 = r + wallet = Wallet_2of3(self.storage) + + if Wallet.is_seed(text1): + xpriv, xpub = bip32_root(text1) + elif Wallet.is_mpk(text1): + xpub = text1 + wallet.add_master_public_key("m/", xpub) + + if Wallet.is_seed(text2): + xpriv2, xpub2 = bip32_root(text2) + elif Wallet.is_mpk(text2): + xpub2 = text2 + wallet.add_master_public_key("cold/", xpub2) + + run_hook('restore_third_key', wallet, self) + + wallet.create_accounts(None) + + + else: raise diff --git a/gui/qt/seed_dialog.py b/gui/qt/seed_dialog.py index 8d45503..9a4376f 100644 --- a/gui/qt/seed_dialog.py +++ b/gui/qt/seed_dialog.py @@ -29,15 +29,25 @@ class SeedDialog(QDialog): QDialog.__init__(self, parent) self.setModal(1) self.setWindowTitle('Electrum' + ' - ' + _('Seed')) - vbox = make_seed_dialog(seed) + vbox = show_seed_box(seed) if imported_keys: vbox.addWidget(QLabel(""+_("WARNING")+": " + _("Your wallet contains imported keys. These keys cannot be recovered from seed.") + "

")) vbox.addLayout(close_button(self)) self.setLayout(vbox) +def icon_filename(sid): + if sid == 'cold': + return ":icons/cold_seed.png" + elif sid == 'hot': + return ":icons/hot_seed.png" + else: + return ":icons/seed.png" + + -def make_seed_dialog(seed, sid=None): + +def show_seed_box(seed, sid=None): save_msg = _("Please save these %d words on paper (order is important).")%len(seed.split()) + " " qr_msg = _("Your seed is also displayed as QR code, in case you want to transfer it to a mobile phone.") + "

" @@ -55,7 +65,7 @@ def make_seed_dialog(seed, sid=None): + _("This seed will be permanently deleted from your wallet file. Make sure you have saved it before you press 'next'") + " " \ elif sid == 'hot': - msg = _("Your main seed is") + msg = _("Your hot seed is") msg2 = save_msg + " " \ + _("If you ever need to recover your wallet from seed, you will need both this seed and your cold seed.") + " " \ @@ -68,7 +78,8 @@ def make_seed_dialog(seed, sid=None): label2.setWordWrap(True) logo = QLabel() - logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56)) + + logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56)) logo.setMaximumWidth(60) grid = QGridLayout() @@ -83,3 +94,32 @@ def make_seed_dialog(seed, sid=None): vbox.addStretch(1) return vbox + + +def enter_seed_box(is_restore, sid=None): + + vbox = QVBoxLayout() + if is_restore: + msg = _("Please enter your wallet seed, or master public key") + "\n" + else: + msg = _("Your seed is important!") \ + + "\n" + _("To make sure that you have properly saved your seed, please retype it here.") + + logo = QLabel() + logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56)) + logo.setMaximumWidth(60) + + label = QLabel(msg) + label.setWordWrap(True) + + seed_e = QTextEdit() + seed_e.setMaximumHeight(100) + + vbox.addWidget(label) + + grid = QGridLayout() + grid.addWidget(logo, 0, 0) + grid.addWidget(seed_e, 0, 1) + + vbox.addLayout(grid) + return vbox, seed_e diff --git a/gui/qt/util.py b/gui/qt/util.py index 8e1ea2f..a3ba598 100644 --- a/gui/qt/util.py +++ b/gui/qt/util.py @@ -45,7 +45,7 @@ def close_button(dialog, label=_("Close") ): b.setDefault(True) return hbox -def ok_cancel_buttons(dialog, ok_label=_("OK") ): +def ok_cancel_buttons2(dialog, ok_label=_("OK") ): hbox = QHBoxLayout() hbox.addStretch(1) b = QPushButton(_("Cancel")) @@ -55,6 +55,10 @@ def ok_cancel_buttons(dialog, ok_label=_("OK") ): hbox.addWidget(b) b.clicked.connect(dialog.accept) b.setDefault(True) + return hbox, b + +def ok_cancel_buttons(dialog, ok_label=_("OK") ): + hbox, b = ok_cancel_buttons2(dialog, ok_label) return hbox def text_dialog(parent, title, label, ok_label, default=None): diff --git a/icons.qrc b/icons.qrc index 9bbbfa6..00e3613 100644 --- a/icons.qrc +++ b/icons.qrc @@ -12,6 +12,8 @@ icons/unlock.png icons/preferences.png icons/seed.png + icons/hot_seed.png + icons/cold_seed.png icons/status_connected.png icons/status_disconnected.png icons/status_waiting.png diff --git a/icons/cold_seed.png b/icons/cold_seed.png new file mode 100644 index 0000000000000000000000000000000000000000..0552ea562ce30e79fe54a16de3ca3df6a05eff06 GIT binary patch literal 9883 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 EKZIOnr~m)} literal 0 HcmV?d00001 diff --git a/icons/hot_seed.png b/icons/hot_seed.png new file mode 100644 index 0000000000000000000000000000000000000000..fa11e68796d791f3c19f4181a00c9887556cc330 GIT binary patch literal 9993 zcmV+kC-&HhP)ofBw!$BHMX%WZ<4jIv+rllnfu2%qnWWJ1ICtR zS?}|lN1B;4XJ*dt`+k4x`+J3Qxy+dyf*_psO9B!gf9n0dYh+RI{pELKKLj+O1f1C) zvq%!d4vi#sf99dYj{tAY?-hSW7qtNJ#EV1x@|Ce~`rLN8qp4xN(R~A}fOykZA$;+=;2o+%{^Lt7h|sgjyP(iZC4*E}BUdajlPoYinLCT&i@AG)Q-&oVL z$#dzt0FL4yl;_UmT%p9#;Uq`LlBBam(m4&6LuNxq0GlGQ=jb%Kf|eVP6^0c_aQYi; zZ8k+hwJEfORGR!Mic$}%Y@x*7V>2W&#lwI4-HG2J%Yfx&Xo~@Y^BDjFAb;_Lp?_2q z>BH~1{%x#T)oL1V*%3;~;n@=>W-{#Sk8@-sP9~=f9T`c!^vbBQch3O(?)u}F@4WY# zE)-d!e>B0aS0}jr!O_nar#S%RI$K$NMXPXOb6DNf98@oD3cBcN_o3SBHR3BrX6PSF z?*7Z~%zOZt22zW`1m`ONgqyYsk*(_^|K+fYn|^b92f^0$h|aJ{&xxbxxoIY&Irbiz z=EztgxBGDPzwUp|cmkc{K&C>~cf8Mg$F>cP@7T7naTWwm?H&9b5U@UPs|M8NAnE9vMu$dW)bSzy-a4`1Z-#(K5D~r-| zwIBd|fAO2{FLpFHTz}O?t+QY7nFHfIyL;jrckk5x9vC~*EO0&nK)7|g6uxj>;PHsx z)w^wdBaMDLf>;gWRCM@*Pmlh`&-U^oV8#+&+%n7xH36J*#f|_}zy}02tmd+-yX;r5 zY4u$0bt#=s?iv5n`<^i#01_5(M8F2PfZ(Uz?|Wd=n&9T1Rek`5CR6qYCH!fJB5&WaE`(QA>U{i)@c;Zr<9Ft#cpexAMu17n8qc33 zKvT8^l(WDxX}xy{82$Nvp0Qpt=6O%%-=EYTN@c6^Z40|_*`*L*2b8mX-Qt2OM#Zj-Rvt1~~AEU@d<2`t)w$rAoVjqpz;HeaTi zDb=C0uZ}ceQ$*~t zi9@ZVvvcnlKYM=kyOz!$KVjyd&cw`oszd(rE#bR8zTjKdcDB*j(vBdC6bd>siNf(3 zTftaifK#0PH*FO{y3U_mv9S@=uFx28;#SSCvFp_d_8yDA^5Ba+XbFDOveXNUaMR^4 zelYZB2;AJ(kfoFyCZA2u-k_KJlP}hI94i1g)!Dy!ZSYThUb`pkb0W(E0k4_Dos8x< zI+D!Y`Ec@UmZd*s2|vFW!vFkD!QWRD>CWDcAYP|TX8JgWT_)@|lSe~wCM*DymJ%auR*ggJ;Wv$n`diojuUeXtC4t>b!kpE@d`bN51Jt(s2 z9oE#8(BUJYs>aW*&TzhpZq=tj3Q&qr$)Q zrr`T+cIoTe);H4G>>=!T;CI{1qrvn{+MC>5*6qLAasibZPhtfCZ{%h>o$@C-n>_&7 z6=^o;v#&qKqc2T8Y#Dno%g;a4saNri_xUcBW$_#9SNUiPsI#_6gWJa8kpu(dDF8ZK zJan~r*Wb27xdmvjqKvlX;0`X94Iq5%o$gB&S!!>OGyveVQm>QI923#Z%un|4Gpe!u zH{$D`syYh0Rpomj!v`Z$V^T&!`RNw7B4$CHB&6=f7I2Q-Z(Ix z{?1*$)V^=oC#4z}Q$N>K#9{$J3i}+lM10QKC+s$1mi`Y9C11jf>nAW1EoVB+DsJ5v z`HEMywXd-lX)2ayCYk5ZaGc}g>ESQ!jNc_o6u$s}4XB+hUT(aygTBGUj{dRaj;-s9 z|5VhB5xraWpmIq`$Q$|JDJGy&HaU6ShSz1 zxp`;w`;^PPvn8Z5lgu+6&oL3p5>FR%`v+o=-uI00uw{&9zw!9^{}d(RtAMMs#mlDd zHa2f*qg;qGJu%Xn$!V=4Q|WC@0p~|9Uz_>br+<+69Mw>DX$1h*4VPs$o>-5km2@)M zBAGGzsOIw*xym;>F?Hf8)Z$EB|i~KVk`#moF6n2!HlY*Tr^a z?nlLvMMkHyeHLw2vU~a>d?({G4<-Hy_%3G3*k*lh&H`e(hK{ybFzCP$i6m+9hfBp z>T~*M8~~QEr!2abtj{ji9F>Tl2pBsL^5o8g)9?MnyNh2tJ|4elOLurqg>nB_l6bmU z!YnY6sb89Q{sO?z4Z*0n6^gqN(cwo@1BjwfJ_`XBy(X%cp_&-dYre~g=O%!|cRrlF z>36mzZ%<^3w{^98oy|cNMG`qSmL!_Y&D{U2@hIoeI)X*90d&1wHxr795eoJ^9wkYj z!M@58fVjvr#&`q9ra?6yQTom_gi~8h0KT)^~v*sYRNXG2B z0RTx7%8FQ(Z7~xiGn2)w%J{twwX21mqoWL3R4gpf*<;iQRReMl>@q$L*#@BE-)1f2 zv68@A@+`0zk*xC1UQO?viskE`ENZ5)-_!2p{g=AmV@22OEbZlLm8zkv6|o!x27zPN zF@%}HOmmL(Dp(W%>^?@cTxKMZDb79_O%|AnrW3)re>dTI~4#LoHA{VZZ>oTTwl30{BHX1q0Oj}?}PnZikgKNHeYvV(fI`CTlw6r3oWdH!65KC#_>>G*K zy^zdkv)wzk^)|8n!luR#Y;XRLkGx&|C=kG0tYKSWgEJJQS;Un=4tEP3cU&8Oe(Q!t zcWo5Srbu-wf?~-6JNJ$;I+c0p#lGpkf8e?Dqf|q)%c2w5NsWtTx-?7z+_zn&eDLD# z;6Gj2+g!IU-)WbLL?TE|Gd-)PC)s;soWA2RMyAq_{O3!tFa7GUxxAZd*w|PBz_|ke z!J=~ir+?f3KwH>-^|lR-RTgjs2(EKi)&i=%!{jt05d0Ky* z`sC5Fs?%G}i$g^~m3*xE>+mBarDeM0O?GT zfr&JI$73Y3#lGVc>AS!Ga^~TIQ6{J+ZEZ_WXgFe|_-9y6#@3bD0&EI{*X=1fFX) z@Yc6o=>Nj1W^dQ!TQ=YftplK>mnrKdZNb}p)j^PxY0Ur0XZw=>`M3#&;<*Hd^QA#Vr7434iO!GT;Qmx& z$o2M1<|(ugMFFp>)U6<1XjD|h$W(^m$uxP*IOo7{K6MDG>&)@)Exh+F8$y5C5_Y?K zSNrF*ZEsGJoj3PS*+Bm_8@O)A1>Qf11f0F=TD?>jl$@vrjKMs|-*ROPFt@Get&cGV~24h$ix*Gs*n-fBtIx z+rQXP-#nhg3INV90BYHw0t7GM+p(Ujui4yF4#bh@ybcT_1Bu zXNv54b%KG(^x>bpF!8aU?d2J&ZEKAc04yE=<^w}TO(-=CQDKS7Vc*uxrQIQMozo^a zhP;kWpWD9L>6Fygpo=xlo;j|j!zK~-+2^#f-F;{({lHUWw>^1~r#MX=%~k+#delTM zAmkcQRDe+n4m)7Kd>xm*W3%sr9+$FlU7LSS6jBrg8Uv2HmbU$)37+0Np83{OV;|ai zfSuHLI60+2(h30193W~zQDKT&a5wjr8z`E`F3yHZK@*Qs32^z$zp4~ zP&_u>%o?S$%oUrOCf(k7zhU2*RJ1)HyA){@ssWw;>f}M9FC6AaJ3z$Dx;N5 z`GtK>RyC<}THHrR1KiNM1AxIpeZ2Dg^W!(ZJa8>$>zN7&mJ9&S=fxJn$0KU^k&8R- zX$|-u*#2wRwO{tzH{##ah1g&PA)bnfYaYR`>t~twcOk-^{F|(amV? z(Cp(?-95B6HaGq6ko|WtQ{78$x!d^)0O2D6d*sShO^>&?wcY&I54;z5PrLbw(+M4G zL8*q5yRY3rxa=Y|UhQ8cOX8fF>UNpCkseErfvkY4(%jX_Rqf5UVy<#hSz^oI#q(A8 zZ(Gy)*xGgLdN;r8?Vwu1R&20kJFH#{-Mw{hTHo7C>}YkyE3!Cma>=bK#EThN*9BX* z(%iX*kk=deJ(q9;OK|!7`33;t;}JDn5q|fzJL+HU>H%x-0z#2G7HDm4C7c$pDI!j% zOfV3@>kG~K0J|&_&nLm-1+N!^A=F3#=BdZTjmO1 zu|Z>N{c|pN^+vV$acTk~uY;a79fUfzpaj<=d)L$$Z&1{9vbh2%)(WRpt7u)dhOVG* zYX{;wOARP2k^Tqg69A+KFKYj^KNxuD=C@sAJ`-*-4}eg3?mHU1bDnbAW!&Csgiw&H z@qj|1ME`J{kW^E?*3yLJP>8g&b9;yPCQA)0nPuH?&;qvN=A!?aH^BI=^Er zY}gF(sCh=9aURIbl~KrRqm&w>tR*Rz3K*Gc#nDVOO)g);XLCa$Ygvm5G&iw&Z8sbI zzUwec-Ik0ocs>9?_(;GW_Q>*G8#ix8YW2efn@mG*dJ?o^Rp-B+i-{tXb+nlSXw$pV z^Jc$;bh^O!Oa?;~X>$0`$7)K^np$uN{RF~6^%fV`vxM5spAP_#cCDMpo(`di?<=`wMdeH-^FRSb}6$qsiU|Kz@3nP7MIGbaZl6 zd(%6Xk{Vd<01&_4-FT@}Q9itF#|~(0t_r_Yg0T_vf5S<1WSZ#s3`R{KBiuE*yl5aR z%DloK8craK0+WdX%?|VWS~Lp9ViibUFOim3IvQMCfo97aIP2|XmpcHYMu+{&tGm`> zYY9N>s_H8<)fsBot8MIa#o~f@O4H}|eV(4llFsCrPUILH%d*K`ElFp%h6RE_9Bwz^ zws!u+r(9d3237!YrheN4y=@;-ozCqWFTNOL5z;lYdt|VBV#J3eOXfn8kZ1m@$s&bP zeJA^9EXVXr8pD9+U!9~`?ySoV)Y$>$I7RW3>o)d6 zWl`y&{jhrvs4~>Ft|b(}8+sjdQWlt=&Jtd&Fg24U9?zodW%@@FOi!1%abr*2`T6My zTr~^;5NU3w!{@#XCYmt2l`EXZzDLWI0mS>)x7_A%INMjRSp!y2LL-(Z8ckA|$)42L zpB=S~0fd?wIeZ|>fkTr_PNpapbuu}PUHd2Kb#>zq>TQHNI{+XULUJe-HSlYe9dK6F zz;XqE;#TBObocfm*6d9vTKRihZLb08Bn({AP6!Lg7)BXGHz%uBGI(b1D5aP{mn}@$ zm?za=YX?M3n(>FjwQ9hAHr0T*oM(Ug>JCtAW~IkQU{w$y=&T9tIbVN3p{BN>OqssH zC=*k8)}_7G7{Lj{7_R|9W0PeEw9?{MH)2*Foz(`}%Mk#w*JjRsNfu4x?_|UNMgRa> z-kjwl<7svuo?&gV8JFysef`9tjSg0s#A_yq9BwyWuSdPX#x~0kJZrgu zY|J&}B}szAN2@GgMFj(qFl&6C-Wo$tTmitF8^Y(>y=(2F`0H!Bdk`fNY>KJl#Z#PI zxq1MIl0Y$M(BW9cb;0!j#A7j3mwVR#FBUXPhC#lhBM2grESbU|wR#j-$svC@LeOFF zumE6RVSv-{Th@6!zuVlt8bwhM-7aYFfTniyuX1|SL94^d+kzVbNTpNQ?G9u~La~{# zh7ty2i6m306uGjFP}zd~*wGqY(t^Y7#;MxXOC&Z}3~*M$!pjK&vP~3jZd+r~e>hZ~ z`Jx03o;Upg_DFzaGBNu&5sTqcUD%veaK%z-)74ZIM5)HOPbAC(AY=xGybXTVDzw&k z0xJMG)!%XN`j+2vI-TvIN?KPwfl*JT)VfkDP#LNXr6z8WN@wtRy*O&JhHjf3r`?Xz z>BMC}p-;htr3O?LR25%yBRzh1R}BM5D*!k}%pn2ba@(jEe24|hCG%_EE;#i84*ijmWZ$D2ce8P86TNx>K}$0k30b+Wf01NDifziKP`A;1u=0 zO%!%CwY4q~%+ZMt#fV0e#D`)~m}fKZV+X21pNSE8<@su@;E6VXl}1h{;)FxtS>X$! zKq(OiYyTrMnFYR2{dJu#Hy4Slt6_jeWr1_v2Dn8fC<(&G&;muBr9=UvY{u`6vOyu4 z#~yag4fy=(m#_OXQ;7tz=@~A#a5FB4V-^(UVu5VmKD_B>%DREB8^|_!eicvwfI}rB z(?D^S0Ty-Z+;bfO;#*q67rVW_1y9h0`gNn3`VSqQ{m3b6pbAxB$q7J*)NxrC|>pGGoA*%|BkvO`p zBgRC05f9kknCk89xqwh8Tvz^8XKoaV=vLgJI8PnniK8r>mEF^q2LqIhvd`(NpJ64f zRQ+K#5pQdiA&i+Ex+RnaRl@5ScE|iio}$=r+ij$CdGvA_5pyZ|EMljou`Qt+c+LZ$ z!6t9_1pM>xYPr7d?=%j*R6>^Kx1U8pK$0a|L;m^S9JIU@O#tQ~$ycg=%0`)VE>~9tBuf&qU8!>i3tFk}z@b7m3)w7se%21qscu)Z zr~p{b3qv5Mfy-t#dWe`SbC?{RKmK3LTX#f6Pu1^mF4v@h^%KCLX(eoy&`2dd3VWC1VUFGkuou&&n1x=R5-l!byHYWwozb?eu|bfx?hX8iofTHiDx92EZoE z*n(Of*|5j)z_GA<|0wXM@>609wS;POjF&xY|%HXI_DXn49Q$33zlll6*ZED zJfbMj;BeqBsgO;B+Y)>xx$woT_`f~u!d`TmMI^5gB&w(fzt3AO|5|7?H<|Q5W)_CT zrluH3W(GKen@=ov0F*PO(yQ5Y`ud76CTr+Dua!_^4n$crXLpT|=d<`#yB$Z^Wm^2U zg@q~A7>P#NAXkQoOcp7dqtVkaH~wE4EiEuSH2W|*InHCT;$G@|3a@wpZ(tY+P1`#& zWu|jWC#X-OTqc{#L(wn=e`1C&UYHd=K(}j4wmV|+^X)ZsDN`G0DGrmucVVEtxNNS(q#mQXXk(O zJZ@a>H6nB#)k(nXr7;{L;P)ZRa-Ekj*Dtg~=Nhj@A5M(n^q;TcR~zpx7>+Tq`QFgkqCtQ^0Op zeW9qwQ4?7dMQB?E4k!2nFfn2N4ThK+8R1_C;y*pHZ@mBj28u~UK~%=vcPP(US|WbV z+WC8+`;SYx9UKdOsztUdT>w#1XA`uv{JP_R=J~hqo;K0m%S$>MS$57hR z|0^9rP89$^A)YUO|IogDuX%E*s6jM)5+!dlqlO+22%`D8bFB#qeSR6G3wcJSV~h-q zkV$4hH_Wo|k_Nk9p6wE%=QRd)zsS9hKK`&J{P@|V|K$Pz11Ns!(8NE_Oi!m@iv<8m zx#Gegs056PT77`X<-thLbrCh)AezZiG7R)ghG^e@;s*|b<(e`x4gE(k2KqVp<3~8~ z%u~bn#)?0&grDLxZ8dj30|21Z2V+yY{5KEp-8YYrbcW(_hT`$rR&OT-0YE-ymWxkM zLNW=mL_9i!FA$p7ZPX3(y5*uqF?T{muwgKH^cW)t_HoC)k*@&RICKPv!u^DI#K zqa#yyPE1da9Xs4tPtnC9M!AepE)y>nk=j=ycdw@$YNkAusW%MsHNq=t)#YWGG>O#Q zFtTJBjYdgd@TR6?Ne;dAJP#io{O%#juK?2)1LV)<>|ag*C|ffr_4MfEpC8%3pIo*& zyOo+4y&8@nIL)Mu;8ejMLLW~;(Gp-TH+LOjCJnJD*;E>TxW22n%Vxta%ji~vC`l0T z)$|=I6*OLc`bnNUKJx6p#I)~WwuXyZvtK{kH*PtlgAE}657EM-J-*bppWpe^%~xIj zb}Lw8$8Igvo>R;zNtDZFN>eFBMJ8F)U?_$!J+C>bQj$8mY*FF}_D!Ccy73BQob zvuD?{95`}h_~w1Xp91 zJ1pUk12Y!&mzK&6TaFb$I*|K$iG6`1L${XU9>CpR(>tJCYuwcgr%jho zedxan+zq&DL9k#PNRpVZtaI>KKfd|4m6>%IG=`5HW_X}K_uWJN58R*ON0#s>YG!_M zDG7hM0-yqdB9MHfz?1Y1-3Hv9h{asJn=gLD7t7^xjEs)4s=H_2*C&RC81FyA(W#k3 z|Eq82U(#^Mn(`QU^X@YBpxa76n#UteL&Oi%w;lbHgDZFZ%zL~qwnBB zf{_Szd;KDknXz#u1_v0Mnw-W%kpE(#hbSNCTk47@>0A>=fmE?B4!UBuLF6_ z*Ls3qZ1efHU$}KEq1Ulbkfv!o^u6!VwXqkUKY+*WB04e7#LxioRJtPgN@6Ew2|hu! z$m0!L`zxDApE(e?f#4m@?%R9A{@?5BTIcH7*n3hoc<$+)Oh)7Q!(rkx(}?*jgPH8^ zUE|X)+?U~})+~=(N754fn=0^F0l=99fr8ng+joJ$rcZP?eQddd=frMUA=1$b5fa0b7QW%oER_wVz+ z$H{d!ceQ=O)8LK-0%4pkm0Tvv4-XbXNedxNKmz zfZ(FKfrDh}@LbI-&({dNBJ?wI^Hu<`__jfX5o}a9Z!j!@S61zu_L;sE0Pz0-L)L^R TUIRcf00000NkvXXu0mjf!nSu@ literal 0 HcmV?d00001 diff --git a/lib/wallet.py b/lib/wallet.py index 857e903..9d7fff5 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -1854,15 +1854,36 @@ class Wallet(object): if not seed: return False elif is_old_seed(seed): - return OldWallet + return True elif is_new_seed(seed): - return NewWallet + return True else: return False @classmethod + def is_mpk(self, mpk): + try: + int(mpk, 16) + old = True + except: + old = False + + if old: + return len(mpk) == 128 + else: + try: + deserialize_xkey(mpk) + return True + except: + return False + + + @classmethod def from_seed(self, seed, storage): - klass = self.is_seed(seed) + if is_old_seed(seed): + klass = OldWallet + elif is_new_seed(seed): + klass = NewWallet w = klass(storage) w.init_seed(seed) return w -- 1.7.1