Browse Source

http server: add ssl and bip70 signed requests

dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
ThomasV 6 years ago
parent
commit
128285a050
  1. 4
      electrum/commands.py
  2. 31
      electrum/daemon.py
  3. 2
      electrum/gui/kivy/uix/screens.py
  4. 7
      electrum/gui/qt/request_list.py
  5. 4
      electrum/paymentrequest.py
  6. 19
      electrum/wallet.py
  7. 2
      electrum/www

4
electrum/commands.py

@ -702,7 +702,7 @@ class Commands:
@command('w') @command('w')
async def getrequest(self, key): async def getrequest(self, key):
"""Return a payment request""" """Return a payment request"""
r = self.wallet.get_payment_request(key, self.config) r = self.wallet.get_request(key)
if not r: if not r:
raise Exception("Request not found") raise Exception("Request not found")
return self._format_request(r) return self._format_request(r)
@ -754,7 +754,7 @@ class Commands:
expiration = int(expiration) if expiration else None expiration = int(expiration) if expiration else None
req = self.wallet.make_payment_request(addr, amount, memo, expiration) req = self.wallet.make_payment_request(addr, amount, memo, expiration)
self.wallet.add_payment_request(req, self.config) 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) return self._format_request(out)
@command('w') @command('w')

31
electrum/daemon.py

@ -34,6 +34,7 @@ import aiohttp
from aiohttp import web from aiohttp import web
from base64 import b64decode from base64 import b64decode
from collections import defaultdict from collections import defaultdict
import ssl
import jsonrpcclient import jsonrpcclient
import jsonrpcserver import jsonrpcserver
@ -184,18 +185,25 @@ class HttpServer(Logger):
#await self.pending[key].set() #await self.pending[key].set()
async def run(self): 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 = 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.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_invoice', self.get_request)])
app.add_routes([web.get('/api/get_status', self.get_status)]) 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) runner = web.AppRunner(app)
await runner.setup() await runner.setup()
host = self.config.get('http_host', 'localhost') site = web.TCPSite(runner, port=port, host=host, ssl_context=ssl_context)
port = int(self.config.get('http_port'))
site = web.TCPSite(runner, port=port, host=host)
await site.start() await site.start()
async def create_request(self, request): async def create_request(self, request):
@ -207,13 +215,22 @@ class HttpServer(Logger):
message = params['message'] or "donation" message = params['message'] or "donation"
payment_hash = await wallet.lnworker._add_invoice_coro(amount, message, 3600) payment_hash = await wallet.lnworker._add_invoice_coro(amount, message, 3600)
key = payment_hash.hex() 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): async def get_request(self, r):
key = r.query_string key = r.query_string
request = self.daemon.wallet.get_request(key) request = self.daemon.wallet.get_request(key)
return web.json_response(request) 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): async def get_status(self, request):
ws = web.WebSocketResponse() ws = web.WebSocketResponse()
await ws.prepare(request) await ws.prepare(request)

2
electrum/gui/kivy/uix/screens.py

@ -429,7 +429,7 @@ class ReceiveScreen(CScreen):
self.screen.address = addr self.screen.address = addr
def on_address(self, 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 = '' self.screen.status = ''
if req: if req:
self.screen.message = req.get('memo', '') self.screen.message = req.get('memo', '')

7
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'])) menu.addAction(_("Copy lightning payment request"), lambda: self.parent.do_copy('Request', req['invoice']))
else: else:
menu.addAction(_("Copy URI"), lambda: self.parent.do_copy('URI', req['URI'])) menu.addAction(_("Copy URI"), lambda: self.parent.do_copy('URI', req['URI']))
if 'http_url' in req: if 'view_url' in req:
menu.addAction(_("View in web browser"), lambda: webopen(req['http_url'])) menu.addAction(_("View in web browser"), lambda: webopen(req['view_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))
menu.addAction(_("Delete"), lambda: self.parent.delete_request(key)) menu.addAction(_("Delete"), lambda: self.parent.delete_request(key))
run_hook('receive_list_menu', menu, key) run_hook('receive_list_menu', menu, key)
menu.exec_(self.viewport().mapToGlobal(position)) menu.exec_(self.viewport().mapToGlobal(position))

4
electrum/paymentrequest.py

@ -473,8 +473,8 @@ def serialize_request(req):
def make_request(config, req): def make_request(config, req):
pr = make_unsigned_request(req) pr = make_unsigned_request(req)
key_path = config.get('ssl_privkey') key_path = config.get('ssl_keyfile')
cert_path = config.get('ssl_chain') cert_path = config.get('ssl_certfile')
if key_path and cert_path: if key_path and cert_path:
sign_request_with_x509(pr, key_path, cert_path) sign_request_with_x509(pr, key_path, cert_path)
return pr return pr

19
electrum/wallet.py

@ -1268,7 +1268,7 @@ class Abstract_Wallet(AddressSynchronizer):
return True, conf return True, conf
return False, None return False, None
def get_payment_request(self, addr, config): def get_payment_request(self, addr):
r = self.receive_requests.get(addr) r = self.receive_requests.get(addr)
if not r: if not r:
return return
@ -1324,15 +1324,22 @@ class Abstract_Wallet(AddressSynchronizer):
from .simple_config import get_config from .simple_config import get_config
config = get_config() config = get_config()
if key in self.receive_requests: if key in self.receive_requests:
req = self.get_payment_request(key, {}) req = self.get_payment_request(key)
else: else:
req = self.lnworker.get_request(key) req = self.lnworker.get_request(key)
if not req: if not req:
return return
if config.get('http_port', 8000): if config.get('http_port'):
host = config.get('http_host', 'localhost') host = config.get('http_host', 'localhost')
port = config.get('http_port', 8000) port = config.get('http_port')
req['http_url'] = 'http://%s:%d/electrum/index.html?id=%s'%(host, port, key) 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 return req
def receive_tx_callback(self, tx_hash, tx, tx_height): def receive_tx_callback(self, tx_hash, tx, tx_height):
@ -1397,7 +1404,7 @@ class Abstract_Wallet(AddressSynchronizer):
def get_sorted_requests(self, config): def get_sorted_requests(self, config):
""" sorted by timestamp """ """ 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: if self.lnworker:
out += self.lnworker.get_requests() out += self.lnworker.get_requests()
out.sort(key=operator.itemgetter('time')) out.sort(key=operator.itemgetter('time'))

2
electrum/www

@ -1 +1 @@
Subproject commit cfab42bfc1db4976d7bba5a9d20bfc94da91db7f Subproject commit b777d98ce1f374d4136d1481a89b198f9813aa90
Loading…
Cancel
Save