Browse Source

Integrate http_server (previously in electrum-merchant)

Use submodule to fetch HTML and CSS files
dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
ThomasV 5 years ago
parent
commit
747ab7a0a2
  1. 3
      .gitmodules
  2. 1
      electrum/commands.py
  3. 79
      electrum/daemon.py
  4. 2
      electrum/gui/qt/invoice_list.py
  5. 5
      electrum/gui/qt/main_window.py
  6. 63
      electrum/gui/qt/request_list.py
  7. 65
      electrum/wallet.py
  8. 132
      electrum/websockets.py
  9. 1
      electrum/www
  10. 9
      run_electrum

3
.gitmodules

@ -4,3 +4,6 @@
[submodule "contrib/CalinsQRReader"] [submodule "contrib/CalinsQRReader"]
path = contrib/osx/CalinsQRReader path = contrib/osx/CalinsQRReader
url = https://github.com/spesmilo/CalinsQRReader url = https://github.com/spesmilo/CalinsQRReader
[submodule "electrum/www"]
path = electrum/www
url = git@github.com:spesmilo/electrum-http.git

1
electrum/commands.py

@ -1039,7 +1039,6 @@ arg_types = {
config_variables = { config_variables = {
'addrequest': { 'addrequest': {
'requests_dir': 'directory where a bip70 file will be written.',
'ssl_privkey': 'Path to your SSL private key, needed to sign the request.', 'ssl_privkey': 'Path to your SSL private key, needed to sign the request.',
'ssl_chain': 'Chain of SSL certificates, needed for signed requests. Put your certificate at the top and the root CA at the end', 'ssl_chain': 'Chain of SSL certificates, needed for signed requests. Put your certificate at the top and the root CA at the end',
'url_rewrite': 'Parameters passed to str.replace(), in order to create the r= part of bitcoin: URIs. Example: \"(\'file:///var/www/\',\'https://electrum.org/\')\"', 'url_rewrite': 'Parameters passed to str.replace(), in order to create the r= part of bitcoin: URIs. Example: \"(\'file:///var/www/\',\'https://electrum.org/\')\"',

79
electrum/daemon.py

@ -33,6 +33,7 @@ from typing import Dict, Optional, Tuple
import aiohttp import aiohttp
from aiohttp import web from aiohttp import web
from base64 import b64decode from base64 import b64decode
from collections import defaultdict
import jsonrpcclient import jsonrpcclient
import jsonrpcserver import jsonrpcserver
@ -41,6 +42,7 @@ from jsonrpcclient.clients.aiohttp_client import AiohttpClient
from .network import Network from .network import Network
from .util import (json_decode, to_bytes, to_string, profiler, standardize_path, constant_time_compare) from .util import (json_decode, to_bytes, to_string, profiler, standardize_path, constant_time_compare)
from .util import PR_PAID, PR_EXPIRED, get_request_status
from .wallet import Wallet, Abstract_Wallet from .wallet import Wallet, Abstract_Wallet
from .storage import WalletStorage from .storage import WalletStorage
from .commands import known_commands, Commands from .commands import known_commands, Commands
@ -168,6 +170,79 @@ class WatchTowerServer(Logger):
async def add_sweep_tx(self, *args): async def add_sweep_tx(self, *args):
return await self.lnwatcher.sweepstore.add_sweep_tx(*args) return await self.lnwatcher.sweepstore.add_sweep_tx(*args)
class HttpServer(Logger):
def __init__(self, daemon):
Logger.__init__(self)
self.daemon = daemon
self.config = daemon.config
self.pending = defaultdict(asyncio.Event)
self.daemon.network.register_callback(self.on_payment, ['payment_received'])
async def on_payment(self, evt, *args):
print(evt, args)
#await self.pending[key].set()
async def run(self):
from aiohttp import helpers
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')])
runner = web.AppRunner(app)
await runner.setup()
host = self.config.get('http_host', 'localhost')
port = self.config.get('http_port', 8000)
site = web.TCPSite(runner, port=port, host=host)
await site.start()
async def create_request(self, request):
params = await request.post()
wallet = self.daemon.wallet
if 'amount_sat' not in params or not params['amount_sat'].isdigit():
raise web.HTTPUnsupportedMediaType()
amount = int(params['amount_sat'])
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)
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_status(self, request):
ws = web.WebSocketResponse()
await ws.prepare(request)
key = request.query_string
info = self.daemon.wallet.get_request(key)
if not info:
await ws.send_str('unknown invoice')
await ws.close()
return ws
if info.get('status') == PR_PAID:
await ws.send_str(f'already paid')
await ws.close()
return ws
if info.get('status') == PR_EXPIRED:
await ws.send_str(f'invoice expired')
await ws.close()
return ws
while True:
try:
await asyncio.wait_for(self.pending[key].wait(), 1)
break
except asyncio.TimeoutError:
# send data on the websocket, to keep it alive
await ws.send_str('waiting')
await ws.send_str('paid')
await ws.close()
return ws
class AuthenticationError(Exception): class AuthenticationError(Exception):
pass pass
@ -197,6 +272,9 @@ class Daemon(Logger):
if listen_jsonrpc: if listen_jsonrpc:
jobs.append(self.start_jsonrpc(config, fd)) jobs.append(self.start_jsonrpc(config, fd))
# server-side watchtower # server-side watchtower
self.http_server = HttpServer(self)
if self.http_server:
jobs.append(self.http_server.run())
self.watchtower = WatchTowerServer(self.network) if self.config.get('watchtower_host') else None self.watchtower = WatchTowerServer(self.network) if self.config.get('watchtower_host') else None
if self.watchtower: if self.watchtower:
jobs.append(self.watchtower.run) jobs.append(self.watchtower.run)
@ -296,6 +374,7 @@ class Daemon(Logger):
wallet = Wallet(storage) wallet = Wallet(storage)
wallet.start_network(self.network) wallet.start_network(self.network)
self.wallets[path] = wallet self.wallets[path] = wallet
self.wallet = wallet
return wallet return wallet
def add_wallet(self, wallet: Abstract_Wallet): def add_wallet(self, wallet: Abstract_Wallet):

2
electrum/gui/qt/invoice_list.py

@ -151,4 +151,4 @@ class InvoiceList(MyTreeView):
def create_menu_ln_payreq(self, menu, payreq_key): def create_menu_ln_payreq(self, menu, payreq_key):
req = self.parent.wallet.lnworker.invoices[payreq_key][0] req = self.parent.wallet.lnworker.invoices[payreq_key][0]
menu.addAction(_("Copy Lightning invoice"), lambda: self.parent.do_copy('Lightning invoice', req)) menu.addAction(_("Copy Lightning invoice"), lambda: self.parent.do_copy('Lightning invoice', req))
menu.addAction(_("Delete"), lambda: self.parent.delete_lightning_payreq(payreq_key)) menu.addAction(_("Delete"), lambda: self.parent.delete_invoice(payreq_key))

5
electrum/gui/qt/main_window.py

@ -1028,9 +1028,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
return w return w
def delete_request(self, key):
def delete_payment_request(self, addr): self.wallet.delete_request(key)
self.wallet.remove_payment_request(addr, self.config)
self.request_list.update() self.request_list.update()
self.clear_receive_tab() self.clear_receive_tab()

63
electrum/gui/qt/request_list.py

@ -40,13 +40,11 @@ from electrum.bitcoin import COIN
from electrum.lnaddr import lndecode from electrum.lnaddr import lndecode
import electrum.constants as constants import electrum.constants as constants
from .util import MyTreeView, pr_icons, read_QIcon from .util import MyTreeView, pr_icons, read_QIcon, webopen
REQUEST_TYPE_BITCOIN = 0
REQUEST_TYPE_LN = 1
ROLE_REQUEST_TYPE = Qt.UserRole ROLE_REQUEST_TYPE = Qt.UserRole
ROLE_RHASH_OR_ADDR = Qt.UserRole + 1 ROLE_KEY = Qt.UserRole + 1
class RequestList(MyTreeView): class RequestList(MyTreeView):
@ -76,7 +74,7 @@ class RequestList(MyTreeView):
def select_key(self, key): def select_key(self, key):
for i in range(self.model().rowCount()): for i in range(self.model().rowCount()):
item = self.model().index(i, self.Columns.DATE) item = self.model().index(i, self.Columns.DATE)
row_key = item.data(ROLE_RHASH_OR_ADDR) row_key = item.data(ROLE_KEY)
if key == row_key: if key == row_key:
self.selectionModel().setCurrentIndex(item, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows) self.selectionModel().setCurrentIndex(item, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows)
break break
@ -85,12 +83,12 @@ class RequestList(MyTreeView):
# TODO use siblingAtColumn when min Qt version is >=5.11 # TODO use siblingAtColumn when min Qt version is >=5.11
item = self.model().itemFromIndex(idx.sibling(idx.row(), self.Columns.DATE)) item = self.model().itemFromIndex(idx.sibling(idx.row(), self.Columns.DATE))
request_type = item.data(ROLE_REQUEST_TYPE) request_type = item.data(ROLE_REQUEST_TYPE)
key = item.data(ROLE_RHASH_OR_ADDR) key = item.data(ROLE_KEY)
is_lightning = request_type == REQUEST_TYPE_LN req = self.wallet.get_request(key)
req = self.wallet.get_request(key, is_lightning)
if req is None: if req is None:
self.update() self.update()
return return
is_lightning = request_type == PR_TYPE_LN
text = req.get('invoice') if is_lightning else req.get('URI') text = req.get('invoice') if is_lightning else req.get('URI')
self.parent.receive_address_e.setText(text) self.parent.receive_address_e.setText(text)
@ -101,9 +99,9 @@ class RequestList(MyTreeView):
date_idx = idx.sibling(idx.row(), self.Columns.DATE) date_idx = idx.sibling(idx.row(), self.Columns.DATE)
date_item = m.itemFromIndex(date_idx) date_item = m.itemFromIndex(date_idx)
status_item = m.itemFromIndex(idx) status_item = m.itemFromIndex(idx)
key = date_item.data(ROLE_RHASH_OR_ADDR) key = date_item.data(ROLE_KEY)
is_lightning = date_item.data(ROLE_REQUEST_TYPE) == REQUEST_TYPE_LN is_lightning = date_item.data(ROLE_REQUEST_TYPE) == PR_TYPE_LN
req = self.wallet.get_request(key, is_lightning) req = self.wallet.get_request(key)
if req: if req:
status = req['status'] status = req['status']
status_str = get_request_status(req) status_str = get_request_status(req)
@ -121,7 +119,7 @@ class RequestList(MyTreeView):
if status == PR_PAID: if status == PR_PAID:
continue continue
is_lightning = req['type'] == PR_TYPE_LN is_lightning = req['type'] == PR_TYPE_LN
request_type = REQUEST_TYPE_LN if is_lightning else REQUEST_TYPE_BITCOIN request_type = req['type']
timestamp = req.get('time', 0) timestamp = req.get('time', 0)
amount = req.get('amount') amount = req.get('amount')
message = req['message'] if is_lightning else req['memo'] message = req['message'] if is_lightning else req['memo']
@ -133,18 +131,17 @@ class RequestList(MyTreeView):
self.set_editability(items) self.set_editability(items)
items[self.Columns.DATE].setData(request_type, ROLE_REQUEST_TYPE) items[self.Columns.DATE].setData(request_type, ROLE_REQUEST_TYPE)
items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status))) items[self.Columns.STATUS].setIcon(read_QIcon(pr_icons.get(status)))
if request_type == REQUEST_TYPE_LN: if request_type == PR_TYPE_LN:
items[self.Columns.DATE].setData(req['rhash'], ROLE_RHASH_OR_ADDR) items[self.Columns.DATE].setData(req['rhash'], ROLE_KEY)
items[self.Columns.DATE].setIcon(read_QIcon("lightning.png")) items[self.Columns.DATE].setIcon(read_QIcon("lightning.png"))
items[self.Columns.DATE].setData(REQUEST_TYPE_LN, ROLE_REQUEST_TYPE) elif request_type == PR_TYPE_ADDRESS:
else:
address = req['address'] address = req['address']
if address not in domain: if address not in domain:
continue continue
expiration = req.get('exp', None) expiration = req.get('exp', None)
signature = req.get('sig') signature = req.get('sig')
requestor = req.get('name', '') requestor = req.get('name', '')
items[self.Columns.DATE].setData(address, ROLE_RHASH_OR_ADDR) items[self.Columns.DATE].setData(address, ROLE_KEY)
if signature is not None: if signature is not None:
items[self.Columns.DATE].setIcon(read_QIcon("seal.png")) items[self.Columns.DATE].setIcon(read_QIcon("seal.png"))
items[self.Columns.DATE].setToolTip(f'signed by {requestor}') items[self.Columns.DATE].setToolTip(f'signed by {requestor}')
@ -167,13 +164,9 @@ class RequestList(MyTreeView):
item = self.model().itemFromIndex(idx.sibling(idx.row(), self.Columns.DATE)) item = self.model().itemFromIndex(idx.sibling(idx.row(), self.Columns.DATE))
if not item: if not item:
return return
addr = item.data(ROLE_RHASH_OR_ADDR) key = item.data(ROLE_KEY)
request_type = item.data(ROLE_REQUEST_TYPE) request_type = item.data(ROLE_REQUEST_TYPE)
assert request_type in [REQUEST_TYPE_BITCOIN, REQUEST_TYPE_LN] req = self.wallet.get_request(key)
if request_type == REQUEST_TYPE_BITCOIN:
req = self.wallet.receive_requests.get(addr)
elif request_type == REQUEST_TYPE_LN:
req = self.wallet.lnworker.invoices[addr][0]
if req is None: if req is None:
self.update() self.update()
return return
@ -184,19 +177,15 @@ class RequestList(MyTreeView):
if column == self.Columns.AMOUNT: if column == self.Columns.AMOUNT:
column_data = column_data.strip() column_data = column_data.strip()
menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.do_copy(column_title, column_data)) menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.do_copy(column_title, column_data))
if request_type == REQUEST_TYPE_BITCOIN:
self.create_menu_bitcoin_payreq(menu, addr)
elif request_type == REQUEST_TYPE_LN:
self.create_menu_ln_payreq(menu, addr, req)
menu.exec_(self.viewport().mapToGlobal(position))
def create_menu_bitcoin_payreq(self, menu, addr): #menu.addAction(_("Copy Address"), lambda: self.parent.do_copy('Address', addr))
menu.addAction(_("Copy Address"), lambda: self.parent.do_copy('Address', addr)) menu.addAction(_("Copy Request"), lambda: self.parent.do_copy('URI', self.wallet.get_request_URI(addr)))
menu.addAction(_("Copy URI"), lambda: self.parent.do_copy('URI', self.wallet.get_request_URI(addr))) if 'http_url' in req:
menu.addAction(_("Save as BIP70 file"), lambda: self.parent.export_payment_request(addr)) menu.addAction(_("View in web browser"), lambda: webopen(req['http_url']))
menu.addAction(_("Delete"), lambda: self.parent.delete_payment_request(addr))
run_hook('receive_list_menu', menu, addr)
def create_menu_ln_payreq(self, menu, payreq_key, req): # do bip70 only for browser access
menu.addAction(_("Copy Lightning invoice"), lambda: self.parent.do_copy('Lightning invoice', req)) # so, each request should have an ID, regardless
menu.addAction(_("Delete"), lambda: self.parent.delete_lightning_payreq(payreq_key)) #menu.addAction(_("Save as BIP70 file"), lambda: self.parent.export_payment_request(addr))
menu.addAction(_("Delete"), lambda: self.parent.delete_request(key))
run_hook('receive_list_menu', menu, key)
menu.exec_(self.viewport().mapToGlobal(position))

65
electrum/wallet.py

@ -1279,32 +1279,6 @@ class Abstract_Wallet(AddressSynchronizer):
out['status'] = status out['status'] = status
if conf is not None: if conf is not None:
out['confirmations'] = conf out['confirmations'] = conf
# check if bip70 file exists
rdir = config.get('requests_dir')
if rdir:
key = out.get('id', addr)
path = os.path.join(rdir, 'req', key[0], key[1], key)
if os.path.exists(path):
baseurl = 'file://' + rdir
rewrite = config.get('url_rewrite')
if rewrite:
try:
baseurl = baseurl.replace(*rewrite)
except BaseException as e:
self.logger.info(f'Invalid config setting for "url_rewrite". err: {e}')
out['request_url'] = os.path.join(baseurl, 'req', key[0], key[1], key, key)
out['URI'] += '&r=' + out['request_url']
out['index_url'] = os.path.join(baseurl, 'index.html') + '?id=' + key
websocket_server_announce = config.get('websocket_server_announce')
if websocket_server_announce:
out['websocket_server'] = websocket_server_announce
else:
out['websocket_server'] = config.get('websocket_server', 'localhost')
websocket_port_announce = config.get('websocket_port_announce')
if websocket_port_announce:
out['websocket_port'] = websocket_port_announce
else:
out['websocket_port'] = config.get('websocket_port', 9999)
return out return out
def get_request_URI(self, addr): def get_request_URI(self, addr):
@ -1346,11 +1320,19 @@ class Abstract_Wallet(AddressSynchronizer):
status = PR_INFLIGHT if conf <= 0 else PR_PAID status = PR_INFLIGHT if conf <= 0 else PR_PAID
return status, conf return status, conf
def get_request(self, key, is_lightning): def get_request(self, key):
if not is_lightning: 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: else:
req = self.lnworker.get_request(key) req = self.lnworker.get_request(key)
if not req:
return
if config.get('http_port', 8000):
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)
return req return req
def receive_tx_callback(self, tx_hash, tx, tx_height): def receive_tx_callback(self, tx_hash, tx, tx_height):
@ -1389,24 +1371,6 @@ class Abstract_Wallet(AddressSynchronizer):
self.receive_requests[addr] = req self.receive_requests[addr] = req
self.storage.put('payment_requests', self.receive_requests) self.storage.put('payment_requests', self.receive_requests)
self.set_label(addr, message) # should be a default label self.set_label(addr, message) # should be a default label
rdir = config.get('requests_dir')
if rdir and amount is not None:
key = req.get('id', addr)
pr = paymentrequest.make_request(config, req)
path = os.path.join(rdir, 'req', key[0], key[1], key)
if not os.path.exists(path):
try:
os.makedirs(path)
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
with open(os.path.join(path, key), 'wb') as f:
f.write(pr.SerializeToString())
# reload
req = self.get_payment_request(addr, config)
with open(os.path.join(path, key + '.json'), 'w', encoding='utf-8') as f:
f.write(json.dumps(req))
return req return req
def delete_request(self, key): def delete_request(self, key):
@ -1427,14 +1391,7 @@ class Abstract_Wallet(AddressSynchronizer):
def remove_payment_request(self, addr, config): def remove_payment_request(self, addr, config):
if addr not in self.receive_requests: if addr not in self.receive_requests:
return False return False
r = self.receive_requests.pop(addr) self.receive_requests.pop(addr)
rdir = config.get('requests_dir')
if rdir:
key = r.get('id', addr)
for s in ['.json', '']:
n = os.path.join(rdir, 'req', key[0], key[1], key, key + s)
if os.path.exists(n):
os.unlink(n)
self.storage.put('payment_requests', self.receive_requests) self.storage.put('payment_requests', self.receive_requests)
return True return True

132
electrum/websockets.py

@ -1,132 +0,0 @@
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2015 Thomas Voegtlin
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import threading
import os
import json
from collections import defaultdict
import asyncio
from typing import Dict, List, Tuple, TYPE_CHECKING
import traceback
import sys
try:
from SimpleWebSocketServer import WebSocket, SimpleSSLWebSocketServer
except ImportError:
sys.exit("install SimpleWebSocketServer")
from . import bitcoin
from .synchronizer import SynchronizerBase
from .logging import Logger
if TYPE_CHECKING:
from .network import Network
from .simple_config import SimpleConfig
request_queue = asyncio.Queue()
class ElectrumWebSocket(WebSocket, Logger):
def __init__(self):
WebSocket.__init__(self)
Logger.__init__(self)
def handleMessage(self):
assert self.data[0:3] == 'id:'
self.logger.info(f"message received {self.data}")
request_id = self.data[3:]
asyncio.run_coroutine_threadsafe(
request_queue.put((self, request_id)), asyncio.get_event_loop())
def handleConnected(self):
self.logger.info(f"connected {self.address}")
def handleClose(self):
self.logger.info(f"closed {self.address}")
class BalanceMonitor(SynchronizerBase):
def __init__(self, config: 'SimpleConfig', network: 'Network'):
SynchronizerBase.__init__(self, network)
self.config = config
self.expected_payments = defaultdict(list) # type: Dict[str, List[Tuple[WebSocket, int]]]
def make_request(self, request_id):
# read json file
rdir = self.config.get('requests_dir')
n = os.path.join(rdir, 'req', request_id[0], request_id[1], request_id, request_id + '.json')
with open(n, encoding='utf-8') as f:
s = f.read()
d = json.loads(s)
addr = d.get('address')
amount = d.get('amount')
return addr, amount
async def main(self):
# resend existing subscriptions if we were restarted
for addr in self.expected_payments:
await self._add_address(addr)
# main loop
while True:
ws, request_id = await request_queue.get()
try:
addr, amount = self.make_request(request_id)
except Exception:
self.logger.exception('')
continue
self.expected_payments[addr].append((ws, amount))
await self._add_address(addr)
async def _on_address_status(self, addr, status):
self.logger.info(f'new status for addr {addr}')
sh = bitcoin.address_to_scripthash(addr)
balance = await self.network.get_balance_for_scripthash(sh)
for ws, amount in self.expected_payments[addr]:
if not ws.closed:
if sum(balance.values()) >= amount:
ws.sendMessage('paid')
class WebSocketServer(threading.Thread):
def __init__(self, config: 'SimpleConfig', network: 'Network'):
threading.Thread.__init__(self)
self.config = config
self.network = network
asyncio.set_event_loop(network.asyncio_loop)
self.daemon = True
self.balance_monitor = BalanceMonitor(self.config, self.network)
self.start()
def run(self):
asyncio.set_event_loop(self.network.asyncio_loop)
host = self.config.get('websocket_server')
port = self.config.get('websocket_port', 9999)
certfile = self.config.get('ssl_chain')
keyfile = self.config.get('ssl_privkey')
self.server = SimpleSSLWebSocketServer(host, port, ElectrumWebSocket, certfile, keyfile)
self.server.serveforever()

1
electrum/www

@ -0,0 +1 @@
Subproject commit 538fa508d41512e670fb84970f821a5db71836d9

9
run_electrum

@ -375,15 +375,6 @@ if __name__ == '__main__':
# run daemon # run daemon
init_plugins(config, 'cmdline') init_plugins(config, 'cmdline')
d = daemon.Daemon(config, fd) d = daemon.Daemon(config, fd)
if config.get('websocket_server'):
from electrum import websockets
websockets.WebSocketServer(config, d.network)
if config.get('requests_dir'):
path = os.path.join(config.get('requests_dir'), 'index.html')
if not os.path.exists(path):
print("Requests directory not configured.")
print("You can configure it using https://github.com/spesmilo/electrum-merchant")
sys_exit(1)
d.run_daemon() d.run_daemon()
sys_exit(0) sys_exit(0)
else: else:

Loading…
Cancel
Save