Browse Source

extend bitcoin: URI with signature data, instead of serialized format

283
ThomasV 10 years ago
parent
commit
e77f0c98e7
  1. 4
      gui/qt/__init__.py
  2. 78
      gui/qt/main_window.py
  3. 2
      gui/qt/paytoedit.py
  4. 8
      lib/paymentrequest.py
  5. 11
      lib/util.py
  6. 5
      plugins/email.py

4
gui/qt/__init__.py

@ -57,7 +57,7 @@ class OpenFileEventFilter(QObject):
def eventFilter(self, obj, event): def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.FileOpen: if event.type() == QtCore.QEvent.FileOpen:
if len(self.windows) >= 1: if len(self.windows) >= 1:
self.windows[0].pay_from_URI(event.url().toEncoded()) self.windows[0].pay_to_URI(event.url().toEncoded())
return True return True
return False return False
@ -140,7 +140,7 @@ class ElectrumGui:
return int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7 return int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7
def set_url(self, uri): def set_url(self, uri):
self.current_window.pay_from_URI(uri) self.current_window.pay_to_URI(uri)
def run_wizard(self, storage, action): def run_wizard(self, storage, action):
import installwizard import installwizard

78
gui/qt/main_window.py

@ -698,26 +698,29 @@ class ElectrumWindow(QMainWindow):
self.update_receive_tab() self.update_receive_tab()
self.clear_receive_tab() self.clear_receive_tab()
def get_receive_URI(self): def get_request_URI(self, addr):
addr = str(self.receive_address_e.text()) req = self.wallet.receive_requests[addr]
amount = self.receive_amount_e.get_amount() message = self.wallet.labels.get(addr, '')
message = unicode(self.receive_message_e.text()) amount = req['amount']
URI = util.create_URI(addr, amount, message) URI = util.create_URI(addr, amount, message)
return URI if req.get('id') and req.get('sig'):
sig = req.get('sig').decode('hex')
sig = bitcoin.base_encode(sig, base=58)
URI += "&id=" + req['id'] + "&sig="+sig
if req.get('timestamp'):
URI += "&timestamp=%d"%req.get('timestamp')
if req.get('expiration'):
URI += "&expiration=%d"%req.get('expiration')
return str(URI)
def receive_list_menu(self, position): def receive_list_menu(self, position):
item = self.receive_list.itemAt(position) item = self.receive_list.itemAt(position)
addr = str(item.text(2)) addr = str(item.text(2))
req = self.wallet.receive_requests[addr] req = self.wallet.receive_requests[addr]
time, amount = req['timestamp'], req['amount']
message = self.wallet.labels.get(addr, '')
URI = util.create_URI(addr, amount, message)
menu = QMenu() menu = QMenu()
menu.addAction(_("Copy Address"), lambda: self.app.clipboard().setText(addr)) menu.addAction(_("Copy Address"), lambda: self.view_and_paste(_('Address'), '', addr))
menu.addAction(_("Copy URI"), lambda: self.app.clipboard().setText(str(URI))) menu.addAction(_("Copy URI"), lambda: self.view_and_paste('URI', '', self.get_request_URI(addr)))
if req.get('signature'): menu.addAction(_("Save as BIP70 file"), lambda: self.export_payment_request(addr))
menu.addAction(_("Copy Signed URI"), lambda: self.view_signed_request(addr))
menu.addAction(_("Save as BIP70 file"), lambda: self.export_payment_request(addr)) #.setEnabled(amount is not None)
menu.addAction(_("Delete"), lambda: self.delete_payment_request(item)) menu.addAction(_("Delete"), lambda: self.delete_payment_request(item))
run_hook('receive_list_menu', menu, addr) run_hook('receive_list_menu', menu, addr)
menu.exec_(self.receive_list.viewport().mapToGlobal(position)) menu.exec_(self.receive_list.viewport().mapToGlobal(position))
@ -745,8 +748,8 @@ class ElectrumWindow(QMainWindow):
return return
pr, requestor = paymentrequest.make_request(self.config, req, alias, alias_privkey) pr, requestor = paymentrequest.make_request(self.config, req, alias, alias_privkey)
if requestor: if requestor:
req['requestor'] = requestor req['id'] = requestor
req['signature'] = pr.signature.encode('hex') req['sig'] = pr.signature.encode('hex')
self.wallet.add_payment_request(req, self.config) self.wallet.add_payment_request(req, self.config)
def save_payment_request(self): def save_payment_request(self):
@ -765,29 +768,20 @@ class ElectrumWindow(QMainWindow):
self.update_address_tab() self.update_address_tab()
self.save_request_button.setEnabled(False) self.save_request_button.setEnabled(False)
def view_signed_request(self, addr): def view_and_paste(self, title, msg, data):
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 = QDialog(self)
dialog.setWindowTitle(_("Signed Request")) dialog.setWindowTitle(title)
vbox = QVBoxLayout() vbox = QVBoxLayout()
pr_e = ShowQRTextEdit(text=pr_text) label = QLabel(msg)
pr_e.addCopyButton(self.app) label.setWordWrap(True)
msg = ' '.join([_('The following URI contains your payment request signed with your OpenAlias key.'), vbox.addWidget(label)
_('The signature is a proof that the payment was requested by you.')]) pr_e = ShowQRTextEdit(text=data)
l = QLabel(msg)
l.setWordWrap(True)
vbox.addWidget(l)
vbox.addWidget(pr_e) vbox.addWidget(pr_e)
msg = _('Note: This format is experimental and may not be supported by other Bitcoin clients.')
vbox.addWidget(QLabel(msg))
vbox.addLayout(Buttons(CopyCloseButton(pr_e.text, self.app, dialog))) vbox.addLayout(Buttons(CopyCloseButton(pr_e.text, self.app, dialog)))
dialog.setLayout(vbox) dialog.setLayout(vbox)
#print len(data), data
dialog.exec_() dialog.exec_()
def export_payment_request(self, addr): def export_payment_request(self, addr):
r = self.wallet.receive_requests.get(addr) r = self.wallet.receive_requests.get(addr)
pr = paymentrequest.serialize_request(r).SerializeToString() pr = paymentrequest.serialize_request(r).SerializeToString()
@ -883,8 +877,8 @@ class ElectrumWindow(QMainWindow):
message = req.get('memo', '') message = req.get('memo', '')
date = format_time(timestamp) date = format_time(timestamp)
status = req.get('status') status = req.get('status')
signature = req.get('signature') signature = req.get('sig')
requestor = req.get('requestor', '') requestor = req.get('id', '')
amount_str = self.format_amount(amount) if amount else "" amount_str = self.format_amount(amount) if amount else ""
account = '' account = ''
item = QTreeWidgetItem([date, account, address, '', message, amount_str, pr_tooltips.get(status,'')]) item = QTreeWidgetItem([date, account, address, '', message, amount_str, pr_tooltips.get(status,'')])
@ -1343,7 +1337,7 @@ class ElectrumWindow(QMainWindow):
self.show_message(self.payment_request.error) self.show_message(self.payment_request.error)
self.payment_request = None self.payment_request = None
def pay_from_URI(self,URI): def pay_to_URI(self, URI):
if not URI: if not URI:
return return
try: try:
@ -1354,13 +1348,14 @@ class ElectrumWindow(QMainWindow):
self.tabs.setCurrentIndex(1) self.tabs.setCurrentIndex(1)
r = out.get('r') r = out.get('r')
s = out.get('s') sig = out.get('sig')
if r or s: _id = out.get('id')
if r or (_id and sig):
def get_payment_request_thread(): def get_payment_request_thread():
if s: if _id and sig:
from electrum import paymentrequest from electrum import paymentrequest
data = bitcoin.base_decode(s, None, base=58) pr = paymentrequest.serialize_request(out).SerializeToString()
self.payment_request = paymentrequest.PaymentRequest(data) self.payment_request = paymentrequest.PaymentRequest(pr)
else: else:
self.payment_request = get_payment_request(r) self.payment_request = get_payment_request(r)
if self.payment_request.verify(self.contacts): if self.payment_request.verify(self.contacts):
@ -1394,19 +1389,16 @@ class ElectrumWindow(QMainWindow):
self.amount_e.textEdited.emit("") self.amount_e.textEdited.emit("")
def do_clear(self): def do_clear(self):
self.not_enough_funds = False self.not_enough_funds = False
self.payto_e.is_pr = False self.payto_e.is_pr = False
for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]: for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
e.setText('') e.setText('')
e.setFrozen(False) e.setFrozen(False)
self.set_pay_from([]) self.set_pay_from([])
self.update_status() self.update_status()
run_hook('do_clear') run_hook('do_clear')
def set_frozen_state(self, addrs, freeze): def set_frozen_state(self, addrs, freeze):
self.wallet.set_frozen_state(addrs, freeze) self.wallet.set_frozen_state(addrs, freeze)
self.update_address_tab() self.update_address_tab()
@ -2215,7 +2207,7 @@ class ElectrumWindow(QMainWindow):
return return
# if the user scanned a bitcoin URI # if the user scanned a bitcoin URI
if data.startswith("bitcoin:"): if data.startswith("bitcoin:"):
self.pay_from_URI(data) self.pay_to_URI(data)
return return
# else if the user scanned an offline signed tx # else if the user scanned an offline signed tx
# transactions are binary, but qrcode seems to return utf8... # transactions are binary, but qrcode seems to return utf8...

2
gui/qt/paytoedit.py

@ -47,7 +47,7 @@ class PayToEdit(ScanQRTextEdit):
self.errors = [] self.errors = []
self.is_pr = False self.is_pr = False
self.is_alias = False self.is_alias = False
self.scan_f = win.pay_from_URI self.scan_f = win.pay_to_URI
self.update_size() self.update_size()
self.payto_address = None self.payto_address = None

8
lib/paymentrequest.py

@ -318,7 +318,6 @@ def sign_request_with_alias(pr, alias, alias_privkey):
address = bitcoin.address_from_private_key(alias_privkey) address = bitcoin.address_from_private_key(alias_privkey)
compressed = bitcoin.is_compressed(alias_privkey) compressed = bitcoin.is_compressed(alias_privkey)
pr.signature = ec_key.sign_message(message, compressed, address) pr.signature = ec_key.sign_message(message, compressed, address)
return pr
def sign_request_with_x509(pr, key_path, cert_path): def sign_request_with_x509(pr, key_path, cert_path):
@ -336,14 +335,13 @@ def sign_request_with_x509(pr, key_path, cert_path):
hashBytes = bytearray(hashlib.sha256(msgBytes).digest()) hashBytes = bytearray(hashlib.sha256(msgBytes).digest())
sig = rsakey.sign(x509.PREFIX_RSA_SHA256 + hashBytes) sig = rsakey.sign(x509.PREFIX_RSA_SHA256 + hashBytes)
pr.signature = bytes(sig) pr.signature = bytes(sig)
return pr
def serialize_request(req): def serialize_request(req):
pr = make_unsigned_request(req) pr = make_unsigned_request(req)
signature = req.get('signature') signature = req.get('sig')
if signature: requestor = req.get('id')
requestor = req.get('requestor') if requestor and signature:
pr.signature = signature.decode('hex') pr.signature = signature.decode('hex')
pr.pki_type = 'dnssec+btc' pr.pki_type = 'dnssec+btc'
pr.pki_data = str(requestor) pr.pki_data = str(requestor)

11
lib/util.py

@ -261,7 +261,7 @@ def parse_URI(uri):
for k, v in pq.items(): for k, v in pq.items():
if len(v)!=1: if len(v)!=1:
raise Exception('Duplicate Key', k) raise Exception('Duplicate Key', k)
if k not in ['amount', 'label', 'message', 'r', 's']: if k not in ['amount', 'label', 'message', 'r', 'id', 'sig', 'timestamp', 'expiration']:
raise BaseException('Unknown key', k) raise BaseException('Unknown key', k)
out = {k: v[0] for k, v in pq.items()} out = {k: v[0] for k, v in pq.items()}
@ -276,9 +276,16 @@ def parse_URI(uri):
amount = Decimal(m.group(1)) * pow( Decimal(10) , k) amount = Decimal(m.group(1)) * pow( Decimal(10) , k)
else: else:
amount = Decimal(am) * COIN amount = Decimal(am) * COIN
out['amount'] = amount out['amount'] = int(amount)
if 'message' in out: if 'message' in out:
out['message'] = out['message'].decode('utf8') out['message'] = out['message'].decode('utf8')
out['memo'] = out['message']
if 'timestamp' in out:
out['timestamp'] = int(out['timestamp'])
out['expiration'] = int(out['expiration'])
if 'sig' in out:
out['sig'] = bitcoin.base_decode(out['sig'], None, base=58).encode('hex')
return out return out

5
plugins/email.py

@ -148,7 +148,10 @@ class Plugin(BasePlugin):
from electrum import paymentrequest from electrum import paymentrequest
r = self.wallet.receive_requests.get(addr) r = self.wallet.receive_requests.get(addr)
message = r.get('memo', '') message = r.get('memo', '')
pr, requestor = paymentrequest.make_request(self.config, r) if r.get('signature'):
pr = paymentrequest.serialize_request(r)
else:
pr, requestor = paymentrequest.make_request(self.config, r)
if not pr: if not pr:
return return
recipient, ok = QtGui.QInputDialog.getText(self.win, 'Send request', 'Send request to:') recipient, ok = QtGui.QInputDialog.getText(self.win, 'Send request', 'Send request to:')

Loading…
Cancel
Save