URI: 
       tMerge branch 'master' into feature/label_sync - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 884371eeddffe8dc270f7af33d88687ad5a38a92
   DIR parent e4ec690ffd70956331ea2e04aee1f963c3d19f68
  HTML Author: Maran <maran.hidskes@gmail.com>
       Date:   Tue, 12 Mar 2013 16:52:15 +0100
       
       Merge branch 'master' into feature/label_sync
       
       Diffstat:
         M electrum                            |       8 +++++---
         M gui/gui_classic.py                  |      93 +++++++++++++++++++------------
         M gui/i18n.py                         |       2 ++
         M lib/bitcoin.py                      |      38 +++++++++++++++++--------------
         M lib/version.py                      |       2 +-
         M make_packages                       |       7 +++++--
         M plugins/pointofsale.py              |      30 ++++++++++++++++--------------
       
       7 files changed, 107 insertions(+), 73 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -34,8 +34,10 @@ except ImportError:
            sys.exit("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'")
        
        
       +is_android = 'ANDROID_DATA' in os.environ
       +
        # load local module as electrum
       -if os.path.exists("lib"):
       +if os.path.exists("lib") or is_android:
            import imp
            fp, pathname, description = imp.find_module('lib')
            imp.load_module('electrum', fp, pathname, description)
       t@@ -89,8 +91,8 @@ if __name__ == '__main__':
            set_verbosity(options.verbose)
        
            # config is an object passed to the various constructors (wallet, interface, gui)
       -    if 'ANDROID_DATA' in os.environ:
       -        config_options = {'wallet_path':"/sdcard/electrum.dat", 'portable':True, 'verbose':True, 'gui':'android'}
       +    if is_android:
       +        config_options = {'wallet_path':"/sdcard/electrum.dat", 'portable':True, 'verbose':True, 'gui':'android', 'auto_cycle':True}
            else:
                config_options = eval(str(options))
                for k, v in config_options.items():
   DIR diff --git a/gui/gui_classic.py b/gui/gui_classic.py
       t@@ -363,6 +363,24 @@ class ElectrumWindow(QMainWindow):
                    apply(cb, args)
        
        
       +    # custom wrappers for getOpenFileName and getSaveFileName, that remember the path selected by the user
       +    def getOpenFileName(self, title, filter = None):
       +        directory = self.config.get('io_dir', os.path.expanduser('~'))
       +        fileName = unicode( QFileDialog.getOpenFileName(self, title, directory, filter) )
       +        if fileName and directory != os.path.dirname(fileName):
       +            self.config.set_key('io_dir', os.path.dirname(fileName), True)
       +        return fileName
       +
       +    def getSaveFileName(self, title, filename, filter = None):
       +        directory = self.config.get('io_dir', os.path.expanduser('~'))
       +        path = os.path.join( directory, filename )
       +        fileName = unicode( QFileDialog.getSaveFileName(self, title, path, filter) )
       +        if fileName and directory != os.path.dirname(fileName):
       +            self.config.set_key('io_dir', os.path.dirname(fileName), True)
       +        return fileName
       +
       +
       +
            def close(self):
                QMainWindow.close(self)
                self.run_hook('close_main_window', (self,))
       t@@ -808,9 +826,9 @@ class ElectrumWindow(QMainWindow):
                    else:
                        QMessageBox.warning(self, _('Error'), msg, _('OK'))
                else:
       -            filename = 'unsigned_tx_%s' % (time.mktime(time.gmtime()))
       +            filename = label + '.txn' if label else 'unsigned_%s.txn' % (time.mktime(time.gmtime()))
                    try:
       -                fileName = QFileDialog.getSaveFileName(QWidget(), _("Select a transaction filename"), os.path.expanduser('~/%s' % (filename)))
       +                fileName = self.getSaveFileName(_("Select a transaction filename"), filename, "*.txn")
                        with open(fileName,'w') as f:
                            f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
                        QMessageBox.information(self, _('Unsigned transaction created'), _("Unsigned transaction was saved to file:") + " " +fileName, _('OK'))
       t@@ -1667,13 +1685,13 @@ class ElectrumWindow(QMainWindow):
                    if not tx_dict["complete"]:
                        assert "input_info" in tx_dict.keys()
                except:
       -            QMessageBox.critical(None, "Unable to parse transaction", _("Electrum was unable to parse your transaction:"))
       +            QMessageBox.critical(None, "Unable to parse transaction", _("Electrum was unable to parse your transaction"))
                    return None
                return tx_dict
        
        
            def read_tx_from_file(self):
       -        fileName = QFileDialog.getOpenFileName(QWidget(), _("Select your transaction file"), os.path.expanduser('~'))
       +        fileName = self.getOpenFileName(_("Select your transaction file"), "*.txn")
                if not fileName:
                    return
                try:
       t@@ -1690,11 +1708,11 @@ class ElectrumWindow(QMainWindow):
                try:
                    self.wallet.signrawtransaction(tx, input_info, [], password)
                    
       -            fileName = QFileDialog.getSaveFileName(QWidget(), _("Select where to save your signed transaction"), os.path.expanduser('~/signed_tx_%s' % (tx.hash()[0:8])))
       +            fileName = self.getSaveFileName(_("Select where to save your signed transaction"), 'signed_%s.txn' % (tx.hash()[0:8]), "*.txn")
                    if fileName:
                        with open(fileName, "w+") as f:
                            f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
       -                self.show_message(_("Transaction saved succesfully"))
       +                self.show_message(_("Transaction saved successfully"))
                        if dialog:
                            dialog.done(0)
                except BaseException, e:
       t@@ -1704,7 +1722,7 @@ class ElectrumWindow(QMainWindow):
            def send_raw_transaction(self, raw_tx, dialog = ""):
                result, result_message = self.wallet.sendtx( raw_tx )
                if result:
       -            self.show_message("Transaction succesfully sent: %s" % (result_message))
       +            self.show_message("Transaction successfully sent: %s" % (result_message))
                    if dialog:
                        dialog.done(0)
                else:
       t@@ -1747,7 +1765,7 @@ class ElectrumWindow(QMainWindow):
                l = QGridLayout()
                dialog.setLayout(l)
        
       -        l.addWidget(QLabel(_("Transaction status: ")), 3,0)
       +        l.addWidget(QLabel(_("Transaction status:")), 3,0)
                l.addWidget(QLabel(_("Actions")), 4,0)
        
                if tx_dict["complete"] == False:
       t@@ -1779,7 +1797,7 @@ class ElectrumWindow(QMainWindow):
        
                try:
                    select_export = _('Select file to export your private keys to')
       -            fileName = QFileDialog.getSaveFileName(QWidget(), select_export, os.path.expanduser('~/electrum-private-keys.csv'), "*.csv")
       +            fileName = self.getSaveFileName(select_export, 'electrum-private-keys.csv', "*.csv")
                    if fileName:
                        with open(fileName, "w+") as csvfile:
                            transaction = csv.writer(csvfile)
       t@@ -1801,7 +1819,7 @@ class ElectrumWindow(QMainWindow):
        
        
            def do_import_labels(self):
       -        labelsFile = QFileDialog.getOpenFileName(QWidget(), _("Open text file"), util.user_dir(), self.tr("Text Files (labels.dat)"))
       +        labelsFile = self.getOpenFileName(_("Open labels file"), "*.dat")
                if not labelsFile: return
                try:
                    f = open(labelsFile, 'r')
       t@@ -1813,16 +1831,16 @@ class ElectrumWindow(QMainWindow):
                    QMessageBox.information(None, _("Labels imported"), _("Your labels where imported from")+" '%s'" % str(labelsFile))
                except (IOError, os.error), reason:
                    QMessageBox.critical(None, _("Unable to import labels"), _("Electrum was unable to import your labels.")+"\n" + str(reason))
       -        
       +            
        
            def do_export_labels(self):
                labels = self.wallet.labels
                try:
       -            labelsFile = util.user_dir() + '/labels.dat'
       -            f = open(labelsFile, 'w+')
       -            json.dump(labels, f)
       -            f.close()
       -            QMessageBox.information(None, "Labels exported", _("Your labels where exported to")+" '%s'" % str(labelsFile))
       +            fileName = self.getSaveFileName(_("Select file to save your labels"), 'electrum_labels.dat', "*.dat")
       +            if fileName:
       +                with open(fileName, 'w+') as f:
       +                    json.dump(labels, f)
       +                QMessageBox.information(None, "Labels exported", _("Your labels where exported to")+" '%s'" % str(fileName))
                except (IOError, os.error), reason:
                    QMessageBox.critical(None, "Unable to export labels", _("Electrum was unable to export your labels.")+"\n" + str(reason))
        
       t@@ -1871,18 +1889,18 @@ class ElectrumWindow(QMainWindow):
                tabs.addTab(tab1, _('Display') )
        
                nz_label = QLabel(_('Display zeros'))
       -        grid_ui.addWidget(nz_label, 3, 0)
       +        grid_ui.addWidget(nz_label, 0, 0)
                nz_e = QLineEdit()
                nz_e.setText("%d"% self.wallet.num_zeros)
       -        grid_ui.addWidget(nz_e, 3, 1)
       +        grid_ui.addWidget(nz_e, 0, 1)
                msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
       -        grid_ui.addWidget(HelpButton(msg), 3, 2)
       +        grid_ui.addWidget(HelpButton(msg), 0, 2)
                nz_e.textChanged.connect(lambda: numbify(nz_e,True))
                if not self.config.is_modifiable('num_zeros'):
                    for w in [nz_e, nz_label]: w.setEnabled(False)
                
                lang_label=QLabel(_('Language') + ':')
       -        grid_ui.addWidget(lang_label , 8, 0)
       +        grid_ui.addWidget(lang_label, 1, 0)
                lang_combo = QComboBox()
                from i18n import languages
                lang_combo.addItems(languages.values())
       t@@ -1891,8 +1909,8 @@ class ElectrumWindow(QMainWindow):
                except:
                    index = 0
                lang_combo.setCurrentIndex(index)
       -        grid_ui.addWidget(lang_combo, 8, 1)
       -        grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart).')+' '), 8, 2)
       +        grid_ui.addWidget(lang_combo, 1, 1)
       +        grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart).')+' '), 1, 2)
                if not self.config.is_modifiable('language'):
                    for w in [lang_combo, lang_label]: w.setEnabled(False)
        
       t@@ -1900,7 +1918,7 @@ class ElectrumWindow(QMainWindow):
                currencies.insert(0, "None")
        
                cur_label=QLabel(_('Currency') + ':')
       -        grid_ui.addWidget(cur_label , 9, 0)
       +        grid_ui.addWidget(cur_label , 2, 0)
                cur_combo = QComboBox()
                cur_combo.addItems(currencies)
                try:
       t@@ -1908,20 +1926,21 @@ class ElectrumWindow(QMainWindow):
                except:
                    index = 0
                cur_combo.setCurrentIndex(index)
       -        grid_ui.addWidget(cur_combo, 9, 1)
       -        grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes.')+' '), 9, 2)
       +        grid_ui.addWidget(cur_combo, 2, 1)
       +        grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes.')+' '), 2, 2)
                
                view_label=QLabel(_('Receive Tab') + ':')
       -        grid_ui.addWidget(view_label , 10, 0)
       +        grid_ui.addWidget(view_label , 3, 0)
                view_combo = QComboBox()
                view_combo.addItems([_('Simple'), _('Advanced')])
                view_combo.setCurrentIndex(self.expert_mode)
       -        grid_ui.addWidget(view_combo, 10, 1)
       +        grid_ui.addWidget(view_combo, 3, 1)
                hh = _('This selects the interaction mode of the "Receive" tab.')+' ' + '\n\n' \
                     + _('Simple') +   ': ' + _('Show only addresses and labels.') + '\n\n' \
                     + _('Advanced') + ': ' + _('Show address balances and add extra menu items to freeze/prioritize addresses.') + '\n\n' 
                
       -        grid_ui.addWidget(HelpButton(hh), 10, 2)
       +        grid_ui.addWidget(HelpButton(hh), 3, 2)
       +        grid_ui.setRowStretch(4,1)
        
                # wallet tab
                tab2 = QWidget()
       t@@ -1929,11 +1948,6 @@ class ElectrumWindow(QMainWindow):
                grid_wallet.setColumnStretch(0,1)
                tabs.addTab(tab2, _('Wallet') )
                
       -        grid_wallet.addWidget(QLabel(_("Load raw transaction")), 3, 0)
       -        grid_wallet.addWidget(EnterButton(_("From file"), self.do_process_from_file),3,1)
       -        grid_wallet.addWidget(EnterButton(_("From text"), self.do_process_from_text),3,2)
       -        grid_wallet.addWidget(HelpButton(_("This will give you the option to sign or broadcast a transaction based on it's status.")),3,3)
       -
                fee_label = QLabel(_('Transaction fee'))
                grid_wallet.addWidget(fee_label, 0, 0)
                fee_e = QLineEdit()
       t@@ -2002,12 +2016,18 @@ class ElectrumWindow(QMainWindow):
                                      + _('If you give it to someone, they will be able to see your transactions, but not to spend your money.') + ' ' \
                                      + _('If you restore your wallet from it, a watching-only (deseeded) wallet will be created.')), 4, 3)
        
       -        grid_io.setRowStretch(4,1)
       +
       +        grid_io.addWidget(QLabel(_("Load transaction")), 5, 0)
       +        grid_io.addWidget(EnterButton(_("From file"), self.do_process_from_file), 5, 1)
       +        grid_io.addWidget(EnterButton(_("From text"), self.do_process_from_text), 5, 2)
       +        grid_io.addWidget(HelpButton(_("This will give you the option to sign or broadcast a transaction based on it's status.")), 5, 3)
       +
       +        grid_io.setRowStretch(5,1)
        
        
                # plugins
                if self.plugins:
       -            tab5 = QWidget()
       +            tab5 = QScrollArea()
                    grid_plugins = QGridLayout(tab5)
                    grid_plugins.setColumnStretch(0,1)
                    tabs.addTab(tab5, _('Plugins') )
       t@@ -2017,8 +2037,9 @@ class ElectrumWindow(QMainWindow):
                        try:
                            name, description = p.get_info()
                            cb = QCheckBox(name)
       +                    cb.setDisabled(not p.is_available())
                            cb.setChecked(p.is_enabled())
       -                    cb.stateChanged.connect(mk_toggle(cb,p))
       +                    cb.clicked.connect(mk_toggle(cb,p))
                            grid_plugins.addWidget(cb, i, 0)
                            grid_plugins.addWidget(HelpButton(description), i, 2)
                        except:
   DIR diff --git a/gui/i18n.py b/gui/i18n.py
       t@@ -44,10 +44,12 @@ languages = {
            'es':_('Spanish'),
            'fr':_('French'),
            'it':_('Italian'),
       +    'ja':_('Japanese'),
            'lv':_('Latvian'),
            'nl':_('Dutch'),
            'ru':_('Russian'),
            'sl':_('Slovenian'),
       +    'ta':_('Tamil'),
            'vi':_('Vietnamese'),
            'zh':_('Chinese')
            }
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -415,13 +415,10 @@ def CKD_prime(K, c, n):
        class ElectrumSequence:
            """  Privatekey(type,n) = Master_private_key + H(n|S|type)  """
        
       -    def __init__(self, master_public_key, mpk2 = None):
       +    def __init__(self, master_public_key, mpk2 = None, mpk3 = None):
                self.master_public_key = master_public_key
       -        if mpk2:
       -            self.mpk2 = mpk2
       -            self.is_p2sh = True
       -        else:
       -            self.is_p2sh = False
       +        self.mpk2 = mpk2
       +        self.mpk3 = mpk3
        
            @classmethod
            def mpk_from_seed(klass, seed):
       t@@ -443,18 +440,23 @@ class ElectrumSequence:
                return string_to_number( Hash( "%d:%d:"%(n,for_change) + mpk.decode('hex') ) )
        
            def get_address(self, sequence):
       -        if not self.is_p2sh:
       +        if not self.mpk2:
                    pubkey = self.get_pubkey(sequence)
                    address = public_key_to_bc_address( pubkey.decode('hex') )
       -        else:
       +        elif not self.mpk3:
                    pubkey1 = self.get_pubkey(sequence)
                    pubkey2 = self.get_pubkey(sequence, use_mpk2=True)
                    address = Transaction.multisig_script([pubkey1, pubkey2], 2)["address"]
       +        else:
       +            pubkey1 = self.get_pubkey(sequence)
       +            pubkey2 = self.get_pubkey(sequence, mpk = self.mpk2)
       +            pubkey3 = self.get_pubkey(sequence, mpk = self.mpk3)
       +            address = Transaction.multisig_script([pubkey1, pubkey2, pubkey3], 2)["address"]
                return address
        
       -    def get_pubkey(self, sequence, use_mpk2=False):
       +    def get_pubkey(self, sequence, mpk=None):
                curve = SECP256k1
       -        mpk = self.mpk2 if use_mpk2 else self.master_public_key
       +        if mpk is None: mpk = self.master_public_key
                z = self.get_sequence(sequence, mpk)
                master_public_key = ecdsa.VerifyingKey.from_string( mpk.decode('hex'), curve = SECP256k1 )
                pubkey_point = master_public_key.pubkey.point + z*curve.generator
       t@@ -484,21 +486,23 @@ class ElectrumSequence:
                if master_public_key != self.master_public_key:
                    print_error('invalid password (mpk)')
                    raise BaseException('Invalid password')
       -
                return True
        
       -
            def get_input_info(self, sequence):
       -
       -        if not self.is_p2sh:
       +        if not self.mpk2:
                    pk_addr = self.get_address(sequence)
                    redeemScript = None
       -        else:
       +        elif not self.mpk3:
                    pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence,use_mpk2=True)
       +            pubkey2 = self.get_pubkey(sequence,mpk=self.mpk2)
                    pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key
                    redeemScript = Transaction.multisig_script([pubkey1, pubkey2], 2)['redeemScript']
       -
       +        else:
       +            pubkey1 = self.get_pubkey(sequence)
       +            pubkey2 = self.get_pubkey(sequence,mpk=self.mpk2)
       +            pubkey3 = self.get_pubkey(sequence,mpk=self.mpk3)
       +            pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key
       +            redeemScript = Transaction.multisig_script([pubkey1, pubkey2, pubkey3], 2)['redeemScript']
                return pk_addr, redeemScript
        
        
   DIR diff --git a/lib/version.py b/lib/version.py
       t@@ -1,4 +1,4 @@
        ELECTRUM_VERSION = "1.7"    # version of the client package
        PROTOCOL_VERSION = '0.6'    # protocol version requested
        SEED_VERSION     = 4        # bump this every time the seed generation is modified
       -TRANSLATION_ID   = 3958     # version of the wiki page 
       +TRANSLATION_ID   = 3992     # version of the wiki page 
   DIR diff --git a/make_packages b/make_packages
       t@@ -20,10 +20,13 @@ if __name__ == '__main__':
            # android
            os.system('rm -rf dist/e4a-%s'%version)
            os.mkdir('dist/e4a-%s'%version)
       -    shutil.copyfile("electrum",'dist/e4a-%s/electrum.py'%version)
       +    shutil.copyfile("electrum",'dist/e4a-%s/e4a.py'%version)
            shutil.copytree("ecdsa",'dist/e4a-%s/ecdsa'%version)
            shutil.copytree("aes",'dist/e4a-%s/aes'%version)
       -    shutil.copytree("lib",'dist/e4a-%s/electrum'%version)
       +    shutil.copytree("lib",'dist/e4a-%s/lib'%version)
       +    os.mkdir('dist/e4a-%s/gui'%version)
       +    shutil.copy("gui/gui_android.py",'dist/e4a-%s/gui'%version)
       +    shutil.copy("gui/__init__.py",'dist/e4a-%s/gui'%version)
        
            os.chdir("dist")
            # create the zip file
   DIR diff --git a/plugins/pointofsale.py b/plugins/pointofsale.py
       t@@ -92,38 +92,36 @@ class QR_Window(QWidget):
                    
        
        
       -
       +config = {}
        
        def get_info():
            return 'Point of Sale', _('Show QR code window and amounts requested for each address. Add menu item to request amount.')
        
        def init(gui):
       -    gui.requested_amounts = gui.config.get('requested_amounts',{}) 
       -    gui.merchant_name = gui.config.get('merchant_name', 'Invoice')
       +    global config
       +    config = gui.config
       +    gui.requested_amounts = config.get('requested_amounts',{}) 
       +    gui.merchant_name = config.get('merchant_name', 'Invoice')
            gui.qr_window = None
        
       -
       -
       -enabled = False
       -
        def is_enabled():
       -    return False
       +    return config.get('pointofsale') is True
       +
       +def is_available():
       +    return True
        
        def toggle(gui):
       -    global enabled
       -    enabled = not enabled
       -    toggle_QR_window(gui, enabled)
        
       -    if enabled:
       +    if not is_enabled():
                gui.expert_mode = True
                gui.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Request')])
       -
                gui.set_hook('item_changed', item_changed)
                gui.set_hook('current_item_changed', recv_changed)
                gui.set_hook('receive_menu', receive_menu)
                gui.set_hook('update_receive_item', update_receive_item)
                gui.set_hook('timer_actions', timer_actions)
                gui.set_hook('close_main_window', close_main_window)
       +        enabled = True
            else:
                gui.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Tx')])
                gui.unset_hook('item_changed', item_changed)
       t@@ -132,9 +130,13 @@ def toggle(gui):
                gui.unset_hook('update_receive_item', update_receive_item)
                gui.unset_hook('timer_actions', timer_actions)
                gui.unset_hook('close_main_window', close_main_window)
       -        
       +        enabled = False
        
       +    config.set_key('pointofsale', enabled, True)
       +    toggle_QR_window(gui, enabled)
            return enabled
       +    
       +
        
        
        def toggle_QR_window(self, show):