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:
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):