commit 128285a0501d45cb15fd44163d16946d31bf7e6e
parent 9d65120e596afd0e5805ce420b098e8b656e9505
Author: ThomasV <thomasv@electrum.org>
Date: Thu, 5 Sep 2019 10:57:50 +0200
http server: add ssl and bip70 signed requests
Diffstat:
6 files changed, 44 insertions(+), 23 deletions(-)
diff --git a/electrum/commands.py b/electrum/commands.py
@@ -702,7 +702,7 @@ class Commands:
@command('w')
async def getrequest(self, key):
"""Return a payment request"""
- r = self.wallet.get_payment_request(key, self.config)
+ r = self.wallet.get_request(key)
if not r:
raise Exception("Request not found")
return self._format_request(r)
@@ -754,7 +754,7 @@ class Commands:
expiration = int(expiration) if expiration else None
req = self.wallet.make_payment_request(addr, amount, memo, expiration)
self.wallet.add_payment_request(req, self.config)
- out = self.wallet.get_payment_request(addr, self.config)
+ out = self.wallet.get_request(addr)
return self._format_request(out)
@command('w')
diff --git a/electrum/daemon.py b/electrum/daemon.py
@@ -34,6 +34,7 @@ import aiohttp
from aiohttp import web
from base64 import b64decode
from collections import defaultdict
+import ssl
import jsonrpcclient
import jsonrpcserver
@@ -184,18 +185,25 @@ class HttpServer(Logger):
#await self.pending[key].set()
async def run(self):
- from aiohttp import helpers
+ host = self.config.get('http_host', 'localhost')
+ port = self.config.get('http_port')
+ root = self.config.get('http_root', '/r')
+ ssl_keyfile = self.config.get('ssl_keyfile')
+ ssl_certfile = self.config.get('ssl_certfile')
+ if ssl_keyfile and ssl_certfile:
+ ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
+ ssl_context.load_cert_chain(ssl_certfile, ssl_keyfile)
+ else:
+ ssl_context = None
app = web.Application()
- #app.on_response_prepare.append(http_server.on_response_prepare)
app.add_routes([web.post('/api/create_invoice', self.create_request)])
app.add_routes([web.get('/api/get_invoice', self.get_request)])
app.add_routes([web.get('/api/get_status', self.get_status)])
- app.add_routes([web.static('/electrum', 'electrum/www')])
+ app.add_routes([web.get('/bip70/{key}.bip70', self.get_bip70_request)])
+ app.add_routes([web.static(root, 'electrum/www')])
runner = web.AppRunner(app)
await runner.setup()
- host = self.config.get('http_host', 'localhost')
- port = int(self.config.get('http_port'))
- site = web.TCPSite(runner, port=port, host=host)
+ site = web.TCPSite(runner, port=port, host=host, ssl_context=ssl_context)
await site.start()
async def create_request(self, request):
@@ -207,13 +215,22 @@ class HttpServer(Logger):
message = params['message'] or "donation"
payment_hash = await wallet.lnworker._add_invoice_coro(amount, message, 3600)
key = payment_hash.hex()
- raise web.HTTPFound('/electrum/index.html?id=' + key)
+ raise web.HTTPFound(self.root + '/pay?id=' + key)
async def get_request(self, r):
key = r.query_string
request = self.daemon.wallet.get_request(key)
return web.json_response(request)
+ async def get_bip70_request(self, r):
+ from .paymentrequest import make_request
+ key = r.match_info['key']
+ request = self.daemon.wallet.get_request(key)
+ if not request:
+ return web.HTTPNotFound()
+ pr = make_request(self.config, request)
+ return web.Response(body=pr.SerializeToString(), content_type='application/bitcoin-paymentrequest')
+
async def get_status(self, request):
ws = web.WebSocketResponse()
await ws.prepare(request)
diff --git a/electrum/gui/kivy/uix/screens.py b/electrum/gui/kivy/uix/screens.py
@@ -429,7 +429,7 @@ class ReceiveScreen(CScreen):
self.screen.address = addr
def on_address(self, addr):
- req = self.app.wallet.get_payment_request(addr, self.app.electrum_config)
+ req = self.app.wallet.get_request(addr)
self.screen.status = ''
if req:
self.screen.message = req.get('memo', '')
diff --git a/electrum/gui/qt/request_list.py b/electrum/gui/qt/request_list.py
@@ -183,11 +183,8 @@ class RequestList(MyTreeView):
menu.addAction(_("Copy lightning payment request"), lambda: self.parent.do_copy('Request', req['invoice']))
else:
menu.addAction(_("Copy URI"), lambda: self.parent.do_copy('URI', req['URI']))
- if 'http_url' in req:
- menu.addAction(_("View in web browser"), lambda: webopen(req['http_url']))
- # do bip70 only for browser access
- # so, each request should have an ID, regardless
- #menu.addAction(_("Save as BIP70 file"), lambda: self.parent.export_payment_request(addr))
+ if 'view_url' in req:
+ menu.addAction(_("View in web browser"), lambda: webopen(req['view_url']))
menu.addAction(_("Delete"), lambda: self.parent.delete_request(key))
run_hook('receive_list_menu', menu, key)
menu.exec_(self.viewport().mapToGlobal(position))
diff --git a/electrum/paymentrequest.py b/electrum/paymentrequest.py
@@ -473,8 +473,8 @@ def serialize_request(req):
def make_request(config, req):
pr = make_unsigned_request(req)
- key_path = config.get('ssl_privkey')
- cert_path = config.get('ssl_chain')
+ key_path = config.get('ssl_keyfile')
+ cert_path = config.get('ssl_certfile')
if key_path and cert_path:
sign_request_with_x509(pr, key_path, cert_path)
return pr
diff --git a/electrum/wallet.py b/electrum/wallet.py
@@ -1268,7 +1268,7 @@ class Abstract_Wallet(AddressSynchronizer):
return True, conf
return False, None
- def get_payment_request(self, addr, config):
+ def get_payment_request(self, addr):
r = self.receive_requests.get(addr)
if not r:
return
@@ -1324,15 +1324,22 @@ class Abstract_Wallet(AddressSynchronizer):
from .simple_config import get_config
config = get_config()
if key in self.receive_requests:
- req = self.get_payment_request(key, {})
+ req = self.get_payment_request(key)
else:
req = self.lnworker.get_request(key)
if not req:
return
- if config.get('http_port', 8000):
+ if config.get('http_port'):
host = config.get('http_host', 'localhost')
- port = config.get('http_port', 8000)
- req['http_url'] = 'http://%s:%d/electrum/index.html?id=%s'%(host, port, key)
+ port = config.get('http_port')
+ root = config.get('http_root', '/r')
+ use_ssl = bool(config.get('ssl_keyfile'))
+ protocol = 'https' if use_ssl else 'http'
+ base = '%s://%s:%d'%(protocol, host, port)
+ req['view_url'] = base + root + '/pay?id=' + key
+ if use_ssl and 'URI' in req:
+ request_url = base + '/bip70/' + key + '.bip70'
+ req['bip70_url'] = request_url
return req
def receive_tx_callback(self, tx_hash, tx, tx_height):
@@ -1397,7 +1404,7 @@ class Abstract_Wallet(AddressSynchronizer):
def get_sorted_requests(self, config):
""" sorted by timestamp """
- out = [self.get_payment_request(x, config) for x in self.receive_requests.keys()]
+ out = [self.get_request(x) for x in self.receive_requests.keys()]
if self.lnworker:
out += self.lnworker.get_requests()
out.sort(key=operator.itemgetter('time'))