URI: 
       textend bitcoin URIs with signed payment requests passed directly - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 9c30ad3dd5fb10832579e858c3cfdded06444dad
   DIR parent e2185da0942e3d5d57bc76d38498269ffb27a735
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Sat, 18 Jul 2015 18:42:56 +0200
       
       extend bitcoin URIs with signed payment requests passed directly
       
       Diffstat:
         M gui/android.py                      |       3 ++-
         M gui/gtk.py                          |       7 +++++--
         M gui/qt/lite_window.py               |       8 +++++---
         M gui/qt/main_window.py               |     118 ++++++++++++++++++++-----------
         M lib/util.py                         |      30 +++++++++++++-----------------
       
       5 files changed, 100 insertions(+), 66 deletions(-)
       ---
   DIR diff --git a/gui/android.py b/gui/android.py
       t@@ -475,7 +475,8 @@ def make_new_contact():
                data = str(r['extras']['SCAN_RESULT']).strip()
                if data:
                    if re.match('^bitcoin:', data):
       -                address, _, _, _, _ = util.parse_URI(data)
       +                out = util.parse_URI(data)
       +                address = out.get('address')
                    elif is_address(data):
                        address = data
                    else:
   DIR diff --git a/gui/gtk.py b/gui/gtk.py
       t@@ -730,9 +730,12 @@ class ElectrumWindow:
                    entry.modify_base(Gtk.StateType.NORMAL, Gdk.color_parse("#ffffff"))
        
            def set_url(self, url):
       -        payto, amount, label, message, payment_request = parse_URI(url)
       +        out = parse_URI(url)
       +        address = out.get('address')
       +        message = out.get('message')
       +        amount = out.get('amount')
                self.notebook.set_current_page(1)
       -        self.payto_entry.set_text(payto)
       +        self.payto_entry.set_text(address)
                self.message_entry.set_text(message)
                self.amount_entry.set_text(amount)
                self.payto_sig.set_visible(False)
   DIR diff --git a/gui/qt/lite_window.py b/gui/qt/lite_window.py
       t@@ -308,12 +308,14 @@ class MiniWindow(QDialog):
        
            def pay_from_URI(self, URI):
                try:
       -            dest_address, amount, label, message, request_url = util.parse_URI(URI)
       +            out = util.parse_URI(URI)
                except:
                    return
       +        address = out.get('address')
       +        amount = out.get('amount')
                amount_text = str(D(amount) / (10**self.actuator.g.decimal_point))
       -        self.address_input.setText(dest_address)
       -        self.address_field_changed(dest_address)
       +        self.address_input.setText(address)
       +        self.address_field_changed(address)
                self.amount_input.setText(amount_text)
        
            def activate(self):
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -715,21 +715,15 @@ class ElectrumWindow(QMainWindow):
                menu = QMenu()
                menu.addAction(_("Copy Address"), lambda: self.app.clipboard().setText(addr))
                menu.addAction(_("Copy URI"), lambda: self.app.clipboard().setText(str(URI)))
       -        menu.addAction(_("Export as BIP70 file"), lambda: self.export_payment_request(addr)).setEnabled(amount is not None)
       +        if req.get('signature'):
       +            menu.addAction(_("View signed URI"), lambda: self.view_signed_request(addr))
       +        menu.addAction(_("Export as BIP70 file"), lambda: self.export_payment_request(addr)) #.setEnabled(amount is not None)
                menu.addAction(_("Delete"), lambda: self.delete_payment_request(item))
                run_hook('receive_list_menu', menu, addr)
                menu.exec_(self.receive_list.viewport().mapToGlobal(position))
        
       -    def save_payment_request(self):
       -        addr = str(self.receive_address_e.text())
       -        amount = self.receive_amount_e.get_amount()
       -        message = unicode(self.receive_message_e.text())
       -        if not message and not amount:
       -            QMessageBox.warning(self, _('Error'), _('No message or amount'), _('OK'))
       -            return False
       -        i = self.expires_combo.currentIndex()
       -        expiration = map(lambda x: x[1], expiration_values)[i]
       -        req = self.wallet.make_payment_request(addr, amount, message, expiration)
       +    def sign_payment_request(self, addr):
       +        req = self.wallet.receive_requests.get(addr)
                alias = self.config.get('alias')
                alias_privkey = None
                if alias and self.alias_info:
       t@@ -754,16 +748,45 @@ class ElectrumWindow(QMainWindow):
                    req['requestor'] = requestor
                    req['signature'] = pr.signature.encode('hex')
                self.wallet.add_payment_request(req, self.config)
       +
       +    def save_payment_request(self):
       +        addr = str(self.receive_address_e.text())
       +        amount = self.receive_amount_e.get_amount()
       +        message = unicode(self.receive_message_e.text())
       +        if not message and not amount:
       +            QMessageBox.warning(self, _('Error'), _('No message or amount'), _('OK'))
       +            return False
       +        i = self.expires_combo.currentIndex()
       +        expiration = map(lambda x: x[1], expiration_values)[i]
       +        req = self.wallet.make_payment_request(addr, amount, message, expiration)
       +        self.wallet.add_payment_request(req, self.config)
       +        self.sign_payment_request(addr)
                self.update_receive_tab()
                self.update_address_tab()
                self.save_request_button.setEnabled(False)
       -        return pr
       +
       +    def view_signed_request(self, addr):
       +        import urllib
       +        r = self.wallet.receive_requests.get(addr)
       +        pr = paymentrequest.serialize_request(r).SerializeToString()
       +        pr_text = 'bitcoin:?s=' + bitcoin.base_encode(pr, base=58)
       +        dialog = QDialog(self)
       +        dialog.setWindowTitle(_("Signed Request"))
       +        vbox = QVBoxLayout()
       +        pr_e = ShowQRTextEdit(text=pr_text)
       +        pr_e.setMaximumHeight(170)
       +        msg = _('This URI contains the payment request signed with your alias.') + '\n' + _('Note: This feature is experimental')
       +        vbox.addWidget(QLabel(msg))
       +        vbox.addWidget(pr_e)
       +        pr_e.addCopyButton(self.app)
       +        vbox.addLayout(Buttons(CloseButton(dialog)))
       +        dialog.setLayout(vbox)
       +        dialog.exec_()
        
        
            def export_payment_request(self, addr):
                r = self.wallet.receive_requests.get(addr)
       -        pr = paymentrequest.serialize_request(r)
       -        pr = pr.SerializeToString()
       +        pr = paymentrequest.serialize_request(r).SerializeToString()
                name = r['id'] + '.bip70'
                fileName = self.getSaveFileName(_("Select where to save your payment request"), name, "*.bip70")
                if fileName:
       t@@ -1320,42 +1343,51 @@ class ElectrumWindow(QMainWindow):
                if not URI:
                    return
                try:
       -            address, amount, label, message, request_url = util.parse_URI(unicode(URI))
       +            out = util.parse_URI(unicode(URI))
                except Exception as e:
                    QMessageBox.warning(self, _('Error'), _('Invalid bitcoin URI:') + '\n' + str(e), _('OK'))
                    return
       -
                self.tabs.setCurrentIndex(1)
        
       -        if not request_url:
       -            if label:
       -                if self.wallet.labels.get(address) != label:
       -                    if self.question(_('Save label "%(label)s" for address %(address)s ?'%{'label':label,'address':address})):
       -                        if address not in self.wallet.addressbook and not self.wallet.is_mine(address):
       -                            self.wallet.addressbook.append(address)
       -                            self.wallet.set_label(address, label)
       -            else:
       -                label = self.wallet.labels.get(address)
       -            if address:
       -                self.payto_e.setText(label + '  <'+ address +'>' if label else address)
       -            if message:
       -                self.message_e.setText(message)
       -            if amount:
       -                self.amount_e.setAmount(amount)
       -                self.amount_e.textEdited.emit("")
       +        r = out.get('r')
       +        s = out.get('s')
       +        if r or s:
       +            def get_payment_request_thread():
       +                if s:
       +                    from electrum import paymentrequest
       +                    data = bitcoin.base_decode(s, None, base=58)
       +                    self.payment_request = paymentrequest.PaymentRequest(data)
       +                else:
       +                    self.payment_request = get_payment_request(r)
       +                if self.payment_request.verify(self.contacts):
       +                    self.emit(SIGNAL('payment_request_ok'))
       +                else:
       +                    self.emit(SIGNAL('payment_request_error'))
       +            t = threading.Thread(target=get_payment_request_thread)
       +            t.setDaemon(True)
       +            t.start()
       +            self.prepare_for_payment_request()
                    return
        
       -        def get_payment_request_thread():
       -            self.payment_request = get_payment_request(request_url)
       -            if self.payment_request.verify(self.contacts):
       -                self.emit(SIGNAL('payment_request_ok'))
       -            else:
       -                self.emit(SIGNAL('payment_request_error'))
       -
       -        t = threading.Thread(target=get_payment_request_thread)
       -        t.setDaemon(True)
       -        t.start()
       -        self.prepare_for_payment_request()
       +        address = out.get('address')
       +        amount = out.get('amount')
       +        label = out.get('label')
       +        message = out.get('message')
       +        if label:
       +            if self.wallet.labels.get(address) != label:
       +                if self.question(_('Save label "%(label)s" for address %(address)s ?'%{'label':label,'address':address})):
       +                    if address not in self.wallet.addressbook and not self.wallet.is_mine(address):
       +                        self.wallet.addressbook.append(address)
       +                        self.wallet.set_label(address, label)
       +        else:
       +            label = self.wallet.labels.get(address)
       +        if address:
       +            self.payto_e.setText(label + '  <'+ address +'>' if label else address)
       +        if message:
       +            self.message_e.setText(message)
       +        if amount:
       +            self.amount_e.setAmount(amount)
       +            self.amount_e.textEdited.emit("")
        
        
        
   DIR diff --git a/lib/util.py b/lib/util.py
       t@@ -261,29 +261,25 @@ def parse_URI(uri):
            for k, v in pq.items():
                if len(v)!=1:
                    raise Exception('Duplicate Key', k)
       -
       -    amount = label = message = request_url = ''
       -    if 'amount' in pq:
       -        am = pq['amount'][0]
       +        if k not in ['amount', 'label', 'message', 'r', 's']:
       +            raise BaseException('Unknown key', k)
       +
       +    out = {k: v[0] for k, v in pq.items()}
       +    if address:
       +        assert bitcoin.is_address(address)
       +        out['address'] = address
       +    if 'amount' in out:
       +        am = out['amount']
                m = re.match('([0-9\.]+)X([0-9])', am)
                if m:
                    k = int(m.group(2)) - 8
                    amount = Decimal(m.group(1)) * pow(  Decimal(10) , k)
                else:
                    amount = Decimal(am) * COIN
       -    if 'message' in pq:
       -        message = pq['message'][0].decode('utf8')
       -    if 'label' in pq:
       -        label = pq['label'][0]
       -    if 'r' in pq:
       -        request_url = pq['r'][0]
       -
       -    if request_url != '':
       -        return address, amount, label, message, request_url
       -
       -    assert bitcoin.is_address(address)
       -
       -    return address, amount, label, message, request_url
       +        out['amount'] = amount
       +    if 'message' in out:
       +        out['message'] = out['message'].decode('utf8')
       +    return out
        
        
        def create_URI(addr, amount, message):