URI: 
       tMerge branch 'master' of gitorious.org:electrum/electrum - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 796c9927024d96cda2d28c17cfa9828d2d71160b
   DIR parent ca31a9e37297242b1f5b7aa0c8b47a88be4f585c
  HTML Author: thomasv <thomasv@gitorious>
       Date:   Mon, 14 May 2012 13:35:47 +0200
       
       Merge branch 'master' of gitorious.org:electrum/electrum
       
       Diffstat:
         M electrum                            |      75 +++++++++++++++++++++++++------
         M lib/gui.py                          |      53 +++++++++++++++++++------------
         M lib/gui_qt.py                       |     114 ++++++++++++++++++++-----------
         M lib/interface.py                    |       4 ++--
         M lib/version.py                      |       2 +-
         M lib/wallet.py                       |       5 +++--
       
       6 files changed, 175 insertions(+), 78 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -24,8 +24,8 @@ from decimal import Decimal
        
        from electrum import Wallet, SecretToASecret, WalletSynchronizer, format_satoshis
        
       -known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'restore', 'payto', 'sendtx', 'password', 'addresses', 'history', 'label', 'mktx','seed','import','signmessage','verifymessage','eval']
       -offline_commands = ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage', 'eval', 'create', 'addresses', 'import', 'seed']
       +known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'restore', 'payto', 'sendtx', 'password', 'addresses', 'history', 'label', 'mktx','seed','import','signmessage','verifymessage','eval','deseed','reseed']
       +offline_commands = ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage', 'eval', 'create', 'addresses', 'import', 'seed','deseed','reseed']
        protected_commands = ['payto', 'password', 'mktx', 'seed', 'import','signmessage' ]
        
        if __name__ == '__main__':
       t@@ -34,6 +34,7 @@ if __name__ == '__main__':
            parser = OptionParser(usage=usage)
            parser.add_option("-g", "--gui", dest="gui", default="qt", help="gui")
            parser.add_option("-w", "--wallet", dest="wallet_path", help="wallet path (default: electrum.dat)")
       +    parser.add_option("-o", "--offline", action="store_true", dest="offline", default=False, help="remain offline")
            parser.add_option("-a", "--all", action="store_true", dest="show_all", default=False, help="show all addresses")
            parser.add_option("-b", "--balance", action="store_true", dest="show_balance", default=False, help="show the balance at listed addresses")
            parser.add_option("-k", "--keys",action="store_true", dest="show_keys",default=False, help="show the private keys of listed addresses")
       t@@ -135,18 +136,20 @@ if __name__ == '__main__':
                        sys.exit(1)
        
                    wallet.seed = str(seed)
       -            WalletSynchronizer(wallet).start()
       -            print "recovering wallet..."
                    wallet.init_mpk( wallet.seed )
       -            wallet.up_to_date_event.clear()
       -            wallet.up_to_date = False
       -            wallet.update()
       -            if wallet.is_found():
       -                wallet.fill_addressbook()
       -                wallet.save()
       -                print "recovery successful"
       -            else:
       -                print "found no history for this wallet"
       +            if not options.offline:
       +                WalletSynchronizer(wallet).start()
       +                print "recovering wallet..."
       +                wallet.up_to_date_event.clear()
       +                wallet.up_to_date = False
       +                wallet.update()
       +                if wallet.is_found():
       +                    print "recovery successful"
       +                else:
       +                    print "found no history for this wallet"
       +            wallet.fill_addressbook()
       +            wallet.save()
       +            print "Wallet saved in '%s'"%options.wallet_path
                else:
                    wallet.new_seed(None)
                    wallet.init_mpk( wallet.seed )
       t@@ -156,6 +159,7 @@ if __name__ == '__main__':
                    print "Please keep it in a safe place; if you lose it, you will not be able to restore your wallet."
                    print "Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:"
                    print "\""+' '.join(mnemonic.mn_encode(wallet.seed))+"\""
       +            print "Wallet saved in '%s'"%options.wallet_path
        
            # check syntax
            if cmd in ['payto', 'mktx']:
       t@@ -171,7 +175,7 @@ if __name__ == '__main__':
                    cmd = 'help'
        
            # open session
       -    if cmd not in offline_commands:
       +    if cmd not in offline_commands and not options.offline:
                WalletSynchronizer(wallet).start()
                wallet.update()
                wallet.save()
       t@@ -237,6 +241,8 @@ if __name__ == '__main__':
                    print "options: --fee, --fromaddr, --changeaddr"
                elif cmd2 == 'seed':
                    print "show generation seed of your wallet. password protected."
       +        elif cmd2 == 'deseed':
       +            print "remove the seed of your wallet."
                elif cmd2 == 'eval':
                    print "Run python eval() on an object\nSyntax: eval <expression>\nExample: eval \"wallet.aliases\""
        
       t@@ -245,6 +251,47 @@ if __name__ == '__main__':
                seed = wallet.pw_decode( wallet.seed, password)
                print seed, '"'+' '.join(mnemonic.mn_encode(seed))+'"'
        
       +    elif cmd == 'deseed':
       +        if not wallet.seed:
       +            print "Eooro: This wallet has no seed"
       +        elif wallet.use_encryption:
       +            print "Error: This wallet is encrypted"
       +        else:
       +            ns = options.wallet_path+'.seed'
       +            print "Warning: you are going to extract the seed from '%s'\nThe seed will be saved in '%s'"%(options.wallet_path,ns)
       +            if raw_input("Are you sure you want to continue? (y/n) ") in ['y','Y','yes']:
       +                f = open(ns,'w')
       +                f.write(wallet.seed)
       +                f.close()
       +                wallet.seed = ''
       +                wallet.save()
       +                print "Done."
       +            else:
       +                print "Action canceled."
       +
       +    elif cmd == 'reseed':
       +        if wallet.seed:
       +            print "This wallet already has a seed"
       +        else:
       +            ns = options.wallet_path+'.seed'
       +            try:
       +                f = open(ns,'r')
       +                seed = f.read()
       +                f.close()
       +            except:
       +                print "seed file not found"
       +                sys.exit()
       +
       +            mpk = wallet.master_public_key
       +            wallet.seed = seed
       +            wallet.use_encryption = False
       +            wallet.init_mpk(seed)
       +            if mpk == wallet.master_public_key:
       +                wallet.save()
       +                print "done"
       +            else:
       +                print "error: master public key does not match"
       +
            elif cmd == 'validateaddress':
                addr = args[1]
                print wallet.is_valid(addr)
   DIR diff --git a/lib/gui.py b/lib/gui.py
       t@@ -60,6 +60,9 @@ def numbify(entry, is_int = False):
        
        def show_seed_dialog(wallet, password, parent):
            from electrum import mnemonic
       +    if not wallet.seed:
       +        show_message("No seed")
       +        return
            try:
                seed = wallet.pw_decode( wallet.seed, password)
            except:
       t@@ -483,6 +486,10 @@ def password_dialog(parent):
            if result != gtk.RESPONSE_CANCEL: return pw
        
        def change_password_dialog(wallet, parent, icon):
       +    if not wallet.seed:
       +        show_message("No seed")
       +        return
       +
            if parent:
                msg = 'Your wallet is encrypted. Use this dialog to change the password. To disable wallet encryption, enter an empty new password.' if wallet.use_encryption else 'Your wallet keys are not encrypted'
            else:
       t@@ -556,7 +563,9 @@ class ElectrumWindow:
                self.funds_error = False # True if not enough funds
        
                self.window = MyWindow(gtk.WINDOW_TOPLEVEL)
       -        self.window.set_title(APP_NAME + " " + self.wallet.electrum_version)
       +        title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + self.wallet.path
       +        if not self.wallet.seed: title += ' [seedless]'
       +        self.window.set_title(title)
                self.window.connect("destroy", gtk.main_quit)
                self.window.set_border_width(0)
                self.window.connect('mykeypress', gtk.main_quit)
       t@@ -566,7 +575,8 @@ class ElectrumWindow:
        
                self.notebook = gtk.Notebook()
                self.create_history_tab()
       -        self.create_send_tab()
       +        if self.wallet.seed:
       +            self.create_send_tab()
                self.create_recv_tab()
                self.create_book_tab()
                self.create_about_tab()
       t@@ -588,17 +598,18 @@ class ElectrumWindow:
                self.network_button.show()
                self.status_bar.pack_end(self.network_button, False, False)
        
       -        def seedb(w, wallet):
       -            if wallet.use_encryption:
       -                password = password_dialog(self.window)
       -                if not password: return
       -            else: password = None
       -            show_seed_dialog(wallet, password, self.window)
       -        button = gtk.Button('S')
       -        button.connect("clicked", seedb, wallet )
       -        button.set_relief(gtk.RELIEF_NONE)
       -        button.show()
       -        self.status_bar.pack_end(button,False, False)
       +        if self.wallet.seed:
       +            def seedb(w, wallet):
       +                if wallet.use_encryption:
       +                    password = password_dialog(self.window)
       +                    if not password: return
       +                else: password = None
       +                show_seed_dialog(wallet, password, self.window)
       +            button = gtk.Button('S')
       +            button.connect("clicked", seedb, wallet )
       +            button.set_relief(gtk.RELIEF_NONE)
       +            button.show()
       +            self.status_bar.pack_end(button,False, False)
        
                settings_icon = gtk.Image()
                settings_icon.set_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU)
       t@@ -620,12 +631,13 @@ class ElectrumWindow:
                pw_icon.set_size_request(16,16 )
                pw_icon.show()
        
       -        password_button = gtk.Button()
       -        password_button.connect("clicked", lambda x: change_password_dialog(self.wallet, self.window, pw_icon))
       -        password_button.add(pw_icon)
       -        password_button.set_relief(gtk.RELIEF_NONE)
       -        password_button.show()
       -        self.status_bar.pack_end(password_button,False,False)
       +        if self.wallet.seed:
       +            password_button = gtk.Button()
       +            password_button.connect("clicked", lambda x: change_password_dialog(self.wallet, self.window, pw_icon))
       +            password_button.add(pw_icon)
       +            password_button.set_relief(gtk.RELIEF_NONE)
       +            password_button.show()
       +            self.status_bar.pack_end(password_button,False,False)
        
                self.window.add(vbox)
                self.window.show_all()
       t@@ -661,7 +673,8 @@ class ElectrumWindow:
                        
        
                thread.start_new_thread(update_status_bar_thread, ())
       -        thread.start_new_thread(check_recipient_thread, ())
       +        if self.wallet.seed:
       +            thread.start_new_thread(check_recipient_thread, ())
                self.notebook.set_current_page(0)
        
        
   DIR diff --git a/lib/gui_qt.py b/lib/gui_qt.py
       t@@ -157,7 +157,8 @@ class ElectrumWindow(QMainWindow):
        
                self.tabs = tabs = QTabWidget(self)
                tabs.addTab(self.create_history_tab(), 'History')
       -        tabs.addTab(self.create_send_tab(),    'Send')
       +        if self.wallet.seed:
       +            tabs.addTab(self.create_send_tab(),    'Send')
                tabs.addTab(self.create_receive_tab(), 'Receive')
                tabs.addTab(self.create_contacts_tab(),'Contacts')
                tabs.addTab(self.create_wall_tab(),    'Wall')
       t@@ -166,7 +167,9 @@ class ElectrumWindow(QMainWindow):
                self.setCentralWidget(tabs)
                self.create_status_bar()
                self.setGeometry(100,100,840,400)
       -        self.setWindowTitle( 'Electrum ' + self.wallet.electrum_version )
       +        title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + self.wallet.path
       +        if not self.wallet.seed: title += ' [seedless]'
       +        self.setWindowTitle( title )
                self.show()
        
                QShortcut(QKeySequence("Ctrl+W"), self, self.close)
       t@@ -178,8 +181,9 @@ class ElectrumWindow(QMainWindow):
        
        
            def connect_slots(self, sender):
       -        self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
       -        self.previous_payto_e=''
       +        if self.wallet.seed:
       +            self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
       +            self.previous_payto_e=''
        
            def check_recipient(self):
                if self.payto_e.hasFocus():
       t@@ -512,42 +516,62 @@ class ElectrumWindow(QMainWindow):
                    entry.setPalette(palette)
        
        
       +    def get_current_addr(self, is_recv):
       +        if is_recv:
       +            l = self.receive_list
       +        else:
       +            l = self.contacts_list
       +        i = l.currentItem()
       +        if i: 
       +            return unicode( i.text(0) )
       +        else:
       +            return ''
        
        
       -    def clear_buttons(self, hbox):
       -        while hbox.count(): hbox.removeItem(hbox.itemAt(0))
       +    def add_receive_buttons(self):
        
       -    def add_buttons(self, l, hbox, is_recv):
       -        self.clear_buttons(hbox)
       +        l = self.receive_list
       +        hbox = self.receive_buttons_hbox
        
       -        i = l.currentItem()
       -        if not i: return
       -        addr = unicode( i.text(0) )
       +        hbox.addWidget(EnterButton("QR",lambda: self.show_address_qrcode(self.get_current_addr(True))))
       +        hbox.addWidget(EnterButton("Copy to Clipboard", lambda: self.app.clipboard().setText(self.get_current_addr(True))))
        
       -        hbox.addWidget(EnterButton("QR",lambda: self.show_address_qrcode(addr)))
       -        hbox.addWidget(EnterButton("Copy to Clipboard", lambda: self.app.clipboard().setText(addr)))
       -        if is_recv:
       -            def toggle_freeze(addr):
       -                if addr in self.wallet.frozen_addresses:
       -                    self.wallet.frozen_addresses.remove(addr)
       -                else:
       -                    self.wallet.frozen_addresses.append(addr)
       -                self.wallet.save()
       -                self.update_receive_tab()
       +        def toggle_freeze():
       +            addr = self.get_current_addr(True)
       +            if not addr: return
       +            if addr in self.wallet.frozen_addresses:
       +                self.wallet.frozen_addresses.remove(addr)
       +            else:
       +                self.wallet.frozen_addresses.append(addr)
       +            self.wallet.save()
       +            self.update_receive_tab()
        
       -            t = "Unfreeze" if addr in self.wallet.frozen_addresses else "Freeze"
       -            hbox.addWidget(EnterButton(t, lambda: toggle_freeze(addr)))
       +        self.freezeButton = b = EnterButton("Freeze", toggle_freeze)
       +        hbox.addWidget(b)
       +        hbox.addStretch(1)
        
       -        else:
       -            def payto(addr):
       -                if not addr:return
       -                self.tabs.setCurrentIndex(1)
       -                self.payto_e.setText(addr)
       -                self.amount_e.setFocus()
       -            hbox.addWidget(EnterButton('Pay to', lambda: payto(addr)))
       -            hbox.addWidget(EnterButton("New", self.newaddress_dialog))
       +
       +    def add_contacts_buttons(self):
       +        l = self.contacts_list
       +        hbox = self.contacts_buttons_hbox
       +
       +        hbox.addWidget(EnterButton("QR",lambda: self.show_address_qrcode(self.get_current_addr(False))))
       +        hbox.addWidget(EnterButton("Copy to Clipboard", lambda: self.app.clipboard().setText(self.get_current_addr(False))))
       +        def payto():
       +            addr = self.get_current_addr(False)
       +            if not addr:return
       +            self.tabs.setCurrentIndex(1)
       +            self.payto_e.setText(addr)
       +            self.amount_e.setFocus()
       +        hbox.addWidget(EnterButton('Pay to', lambda: payto()))
       +        hbox.addWidget(EnterButton("New", self.newaddress_dialog))
                hbox.addStretch(1)
        
       +    def update_receive_buttons(self):
       +        addr = self.get_current_addr(True)
       +        t = "Unfreeze" if addr in self.wallet.frozen_addresses else "Freeze"
       +        self.freezeButton.setText(t)
       +    
        
            def create_receive_tab(self):
                l = QTreeWidget(self)
       t@@ -573,11 +597,15 @@ class ElectrumWindow(QMainWindow):
                hbox.setSpacing(0)
                buttons.setLayout(hbox)
        
       +
                self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l))
                self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l))
       -        self.connect(l, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), lambda: self.add_buttons(l, hbox, True))
       +        l.selectionModel().currentChanged.connect(self.update_receive_buttons)
       +
                self.receive_list = l
                self.receive_buttons_hbox = hbox
       +        self.add_receive_buttons()
       +
                return w
        
            def create_contacts_tab(self):
       t@@ -606,16 +634,13 @@ class ElectrumWindow(QMainWindow):
                self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l))
                self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l))
                self.connect(l, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), self.show_contact_details)
       -        self.connect(l, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), lambda: self.add_buttons(l, hbox, False))
       -
                self.contacts_list = l
                self.contacts_buttons_hbox = hbox
       +        self.add_contacts_buttons()
                return w
        
            def update_receive_tab(self):
                self.receive_list.clear()
       -        self.clear_buttons(self.receive_buttons_hbox)
       -
                for address in self.wallet.all_addresses():
                    if self.wallet.is_change(address):continue
                    label = self.wallet.labels.get(address,'')
       t@@ -647,7 +672,6 @@ class ElectrumWindow(QMainWindow):
        
            def update_contacts_tab(self):
                self.contacts_list.clear()
       -        self.clear_buttons(self.contacts_buttons_hbox)
        
                for alias, v in self.wallet.aliases.items():
                    s, target = v
       t@@ -674,9 +698,11 @@ class ElectrumWindow(QMainWindow):
            def create_status_bar(self):
                sb = QStatusBar()
                sb.setFixedHeight(35)
       -        sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
       +        if self.wallet.seed:
       +            sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "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", lambda: self.show_seed_dialog(self.wallet, self) ) )
       +        if self.wallet.seed:
       +            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)
       t@@ -695,6 +721,11 @@ class ElectrumWindow(QMainWindow):
            @staticmethod
            def show_seed_dialog(wallet, parent=None):
                from electrum import mnemonic
       +
       +        if not wallet.seed:
       +            QMessageBox.information(parent, 'Message', 'No seed', 'OK')
       +            return
       +
                if wallet.use_encryption:
                    password = parent.password_dialog()
                    if not password: return
       t@@ -852,6 +883,11 @@ class ElectrumWindow(QMainWindow):
        
            @staticmethod
            def change_password_dialog( wallet, parent=None ):
       +
       +        if not wallet.seed:
       +            QMessageBox.information(parent, 'Message', 'No seed', 'OK')
       +            return
       +
                d = QDialog(parent)
                d.setModal(1)
        
   DIR diff --git a/lib/interface.py b/lib/interface.py
       t@@ -218,9 +218,10 @@ class TcpStratumInterface(Interface):
                    self.s.connect(( self.host, self.port))
                    self.is_connected = True
                    self.send([('server.version', [ELECTRUM_VERSION])])
       +            print "Connected to %s:%d"%(self.host,self.port)
                except:
                    self.is_connected = False
       -            print "not connected"
       +            print "Not connected"
        
            def run(self):
                try:
       t@@ -380,7 +381,6 @@ class WalletSynchronizer(threading.Thread):
                        response = self.interface.responses.get()
                        self.handle_response(response)
        
       -            print "disconnected, gui callback"
                    self.wallet.gui_callback()
                    if self.loop:
                        time.sleep(5)
   DIR diff --git a/lib/version.py b/lib/version.py
       t@@ -1,2 +1,2 @@
       -ELECTRUM_VERSION = "0.48a"
       +ELECTRUM_VERSION = "0.49"
        SEED_VERSION = 4  # bump this everytime the seed generation is modified
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -23,13 +23,13 @@ try:
            import ecdsa  
            from ecdsa.util import string_to_number, number_to_string
        except:
       -    print "python-ecdsa does not seem to be installed. Try 'sudo easy_install ecdsa'"
       +    print "python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'"
            sys.exit(1)
        
        try:
            import aes
        except:
       -    print "AES does not seem to be installed. Try 'sudo easy_install slowaes'"
       +    print "AES does not seem to be installed. Try 'sudo pip install slowaes'"
            sys.exit(1)
        
        
       t@@ -308,6 +308,7 @@ class Wallet:
                    self.server = server
                    self.save()
                    self.interface.is_connected = False  # this exits the polling loop
       +            self.interface.poke()
        
            def set_path(self, wallet_path):