electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 9c30ad3dd5fb10832579e858c3cfdded06444dad
parent e2185da0942e3d5d57bc76d38498269ffb27a735
Author: ThomasV <thomasv@gitorious>
Date:   Sat, 18 Jul 2015 18:42:56 +0200

extend bitcoin URIs with signed payment requests passed directly

Diffstat:
Mgui/android.py | 3++-
Mgui/gtk.py | 7+++++--
Mgui/qt/lite_window.py | 8+++++---
Mgui/qt/main_window.py | 118++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mlib/util.py | 30+++++++++++++-----------------
5 files changed, 100 insertions(+), 66 deletions(-)

diff --git a/gui/android.py b/gui/android.py @@ -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: diff --git a/gui/gtk.py b/gui/gtk.py @@ -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) diff --git a/gui/qt/lite_window.py b/gui/qt/lite_window.py @@ -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): diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -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: @@ -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: @@ -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("") diff --git a/lib/util.py b/lib/util.py @@ -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):