mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
experimental-websocket-port: option to create a WebSocket port.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
80a47f1111
commit
ed6eaf9171
@ -56,6 +56,7 @@ On success, an object is returned, containing:
|
|||||||
- **experimental-onion-messages** (boolean, optional): `experimental-onion-messages` field from config or cmdline, or default
|
- **experimental-onion-messages** (boolean, optional): `experimental-onion-messages` field from config or cmdline, or default
|
||||||
- **experimental-offers** (boolean, optional): `experimental-offers` field from config or cmdline, or default
|
- **experimental-offers** (boolean, optional): `experimental-offers` field from config or cmdline, or default
|
||||||
- **experimental-shutdown-wrong-funding** (boolean, optional): `experimental-shutdown-wrong-funding` field from config or cmdline, or default
|
- **experimental-shutdown-wrong-funding** (boolean, optional): `experimental-shutdown-wrong-funding` field from config or cmdline, or default
|
||||||
|
- **experimental-websocket-port** (u16, optional): `experimental-websocket-port` field from config or cmdline, or default
|
||||||
- **rgb** (hex, optional): `rgb` field from config or cmdline, or default (always 6 characters)
|
- **rgb** (hex, optional): `rgb` field from config or cmdline, or default (always 6 characters)
|
||||||
- **alias** (string, optional): `alias` field from config or cmdline, or default
|
- **alias** (string, optional): `alias` field from config or cmdline, or default
|
||||||
- **pid-file** (string, optional): `pid-file` field from config or cmdline, or default
|
- **pid-file** (string, optional): `pid-file` field from config or cmdline, or default
|
||||||
@ -205,4 +206,4 @@ RESOURCES
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
Main web site: <https://github.com/ElementsProject/lightning>
|
Main web site: <https://github.com/ElementsProject/lightning>
|
||||||
[comment]: # ( SHA256STAMP:7bb40fc8fac201b32d9701b02596d0fa59eb14a3baf606439cbf96dc11548ed4)
|
[comment]: # ( SHA256STAMP:47c067588120e0f9a71206313685cebb2a8c515e9b04b688b202d2772c8f8146)
|
||||||
|
@ -517,6 +517,13 @@ about whether to add funds or not to a proposed channel is handled
|
|||||||
automatically by a plugin that implements the appropriate logic for
|
automatically by a plugin that implements the appropriate logic for
|
||||||
your needs. The default behavior is to not contribute funds.
|
your needs. The default behavior is to not contribute funds.
|
||||||
|
|
||||||
|
**experimental-websocket-port**
|
||||||
|
|
||||||
|
Specifying this enables support for accepting incoming WebSocket
|
||||||
|
connections on that port, on any IPv4 and IPv6 addresses you listen
|
||||||
|
to. The normal protocol is expected to be sent over WebSocket binary
|
||||||
|
frames once the connection is upgraded.
|
||||||
|
|
||||||
BUGS
|
BUGS
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -121,6 +121,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "`experimental-shutdown-wrong-funding` field from config or cmdline, or default"
|
"description": "`experimental-shutdown-wrong-funding` field from config or cmdline, or default"
|
||||||
},
|
},
|
||||||
|
"experimental-websocket-port": {
|
||||||
|
"type": "u16",
|
||||||
|
"description": "`experimental-websocket-port` field from config or cmdline, or default"
|
||||||
|
},
|
||||||
"rgb": {
|
"rgb": {
|
||||||
"type": "hex",
|
"type": "hex",
|
||||||
"description": "`rgb` field from config or cmdline, or default",
|
"description": "`rgb` field from config or cmdline, or default",
|
||||||
|
@ -350,7 +350,10 @@ int connectd_init(struct lightningd *ld)
|
|||||||
int hsmfd;
|
int hsmfd;
|
||||||
struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr;
|
struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr;
|
||||||
enum addr_listen_announce *listen_announce = ld->proposed_listen_announce;
|
enum addr_listen_announce *listen_announce = ld->proposed_listen_announce;
|
||||||
const char *websocket_helper_path = "";
|
const char *websocket_helper_path;
|
||||||
|
|
||||||
|
websocket_helper_path = subdaemon_path(tmpctx, ld,
|
||||||
|
"lightning_websocketd");
|
||||||
|
|
||||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
|
||||||
fatal("Could not socketpair for connectd<->gossipd");
|
fatal("Could not socketpair for connectd<->gossipd");
|
||||||
@ -384,7 +387,7 @@ int connectd_init(struct lightningd *ld)
|
|||||||
ld->config.use_v3_autotor,
|
ld->config.use_v3_autotor,
|
||||||
ld->config.connection_timeout_secs,
|
ld->config.connection_timeout_secs,
|
||||||
websocket_helper_path,
|
websocket_helper_path,
|
||||||
0);
|
ld->websocket_port);
|
||||||
|
|
||||||
subd_req(ld->connectd, ld->connectd, take(msg), -1, 0,
|
subd_req(ld->connectd, ld->connectd, take(msg), -1, 0,
|
||||||
connect_init_done, NULL);
|
connect_init_done, NULL);
|
||||||
|
@ -216,6 +216,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
|
|||||||
ld->always_use_proxy = false;
|
ld->always_use_proxy = false;
|
||||||
ld->pure_tor_setup = false;
|
ld->pure_tor_setup = false;
|
||||||
ld->tor_service_password = NULL;
|
ld->tor_service_password = NULL;
|
||||||
|
ld->websocket_port = 0;
|
||||||
|
|
||||||
/*~ This is initialized later, but the plugin loop examines this,
|
/*~ This is initialized later, but the plugin loop examines this,
|
||||||
* so set it to NULL explicitly now. */
|
* so set it to NULL explicitly now. */
|
||||||
|
@ -284,6 +284,9 @@ struct lightningd {
|
|||||||
/* Array of (even) TLV types that we should allow. This is required
|
/* Array of (even) TLV types that we should allow. This is required
|
||||||
* since we otherwise would outright reject them. */
|
* since we otherwise would outright reject them. */
|
||||||
u64 *accept_extra_tlv_types;
|
u64 *accept_extra_tlv_types;
|
||||||
|
|
||||||
|
/* EXPERIMENTAL: websocket port if non-zero */
|
||||||
|
u16 websocket_port;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Turning this on allows a tal allocation to return NULL, rather than aborting.
|
/* Turning this on allows a tal allocation to return NULL, rather than aborting.
|
||||||
|
@ -850,6 +850,21 @@ static char *opt_set_wumbo(struct lightningd *ld)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *opt_set_websocket_port(const char *arg, struct lightningd *ld)
|
||||||
|
{
|
||||||
|
u32 port COMPILER_WANTS_INIT("9.3.0 -O2");
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
err = opt_set_u32(arg, &port);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ld->websocket_port = port;
|
||||||
|
if (ld->websocket_port != port)
|
||||||
|
return tal_fmt(NULL, "'%s' is out of range", arg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static char *opt_set_dual_fund(struct lightningd *ld)
|
static char *opt_set_dual_fund(struct lightningd *ld)
|
||||||
{
|
{
|
||||||
/* Dual funding implies anchor outputs */
|
/* Dual funding implies anchor outputs */
|
||||||
@ -1051,6 +1066,11 @@ static void register_opts(struct lightningd *ld)
|
|||||||
"--subdaemon=hsmd:remote_signer "
|
"--subdaemon=hsmd:remote_signer "
|
||||||
"would use a hypothetical remote signing subdaemon.");
|
"would use a hypothetical remote signing subdaemon.");
|
||||||
|
|
||||||
|
opt_register_arg("--experimental-websocket-port",
|
||||||
|
opt_set_websocket_port, NULL,
|
||||||
|
ld,
|
||||||
|
"experimental: alternate port for peers to connect"
|
||||||
|
" using WebSockets (RFC6455)");
|
||||||
opt_register_logging(ld);
|
opt_register_logging(ld);
|
||||||
opt_register_version();
|
opt_register_version();
|
||||||
|
|
||||||
@ -1463,6 +1483,11 @@ static void add_config(struct lightningd *ld,
|
|||||||
json_add_opt_disable_plugins(response, ld->plugins);
|
json_add_opt_disable_plugins(response, ld->plugins);
|
||||||
} else if (opt->cb_arg == (void *)opt_force_feerates) {
|
} else if (opt->cb_arg == (void *)opt_force_feerates) {
|
||||||
answer = fmt_force_feerates(name0, ld->force_feerates);
|
answer = fmt_force_feerates(name0, ld->force_feerates);
|
||||||
|
} else if (opt->cb_arg == (void *)opt_set_websocket_port) {
|
||||||
|
if (ld->websocket_port)
|
||||||
|
json_add_u32(response, name0,
|
||||||
|
ld->websocket_port);
|
||||||
|
return;
|
||||||
} else if (opt->cb_arg == (void *)opt_important_plugin) {
|
} else if (opt->cb_arg == (void *)opt_important_plugin) {
|
||||||
/* Do nothing, this is already handled by
|
/* Do nothing, this is already handled by
|
||||||
* opt_add_plugin. */
|
* opt_add_plugin. */
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# This file is autogenerated by pip-compile with python 3.8
|
# This file is autogenerated by pip-compile with python 3.8
|
||||||
# To update, run:
|
# To update, run:
|
||||||
#
|
#
|
||||||
# pip-compile --output-file=requirements.lock requirements.in
|
# pip-compile --output-file=requirements.lock requirements.txt
|
||||||
#
|
#
|
||||||
alabaster==0.7.12
|
alabaster==0.7.12
|
||||||
# via sphinx
|
# via sphinx
|
||||||
@ -15,9 +15,9 @@ attrs==21.2.0
|
|||||||
babel==2.9.1
|
babel==2.9.1
|
||||||
# via sphinx
|
# via sphinx
|
||||||
base58==2.0.1
|
base58==2.0.1
|
||||||
# via -r requirements.in
|
# via pyln.proto
|
||||||
bitstring==3.1.9
|
bitstring==3.1.9
|
||||||
# via -r requirements.in
|
# via pyln.proto
|
||||||
certifi==2021.5.30
|
certifi==2021.5.30
|
||||||
# via requests
|
# via requests
|
||||||
cffi==1.14.6
|
cffi==1.14.6
|
||||||
@ -27,17 +27,17 @@ cffi==1.14.6
|
|||||||
charset-normalizer==2.0.6
|
charset-normalizer==2.0.6
|
||||||
# via requests
|
# via requests
|
||||||
cheroot==8.5.2
|
cheroot==8.5.2
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
click==7.1.2
|
click==7.1.2
|
||||||
# via flask
|
# via flask
|
||||||
coincurve==13.0.0
|
coincurve==13.0.0
|
||||||
# via -r requirements.in
|
# via pyln.proto
|
||||||
commonmark==0.9.1
|
commonmark==0.9.1
|
||||||
# via recommonmark
|
# via recommonmark
|
||||||
crc32c==2.2.post0
|
crc32c==2.2.post0
|
||||||
# via -r requirements.in
|
# via -r requirements.txt
|
||||||
cryptography==3.4.8
|
cryptography==3.4.8
|
||||||
# via -r requirements.in
|
# via pyln.proto
|
||||||
docutils==0.17.1
|
docutils==0.17.1
|
||||||
# via
|
# via
|
||||||
# recommonmark
|
# recommonmark
|
||||||
@ -45,15 +45,15 @@ docutils==0.17.1
|
|||||||
entrypoints==0.3
|
entrypoints==0.3
|
||||||
# via flake8
|
# via flake8
|
||||||
ephemeral-port-reserve==1.1.1
|
ephemeral-port-reserve==1.1.1
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
execnet==1.9.0
|
execnet==1.9.0
|
||||||
# via pytest-xdist
|
# via pytest-xdist
|
||||||
flake8==3.7.9
|
flake8==3.7.9
|
||||||
# via -r requirements.in
|
# via -r requirements.txt
|
||||||
flaky==3.7.0
|
flaky==3.7.0
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
flask==1.1.4
|
flask==1.1.4
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
idna==3.2
|
idna==3.2
|
||||||
# via requests
|
# via requests
|
||||||
imagesize==1.2.0
|
imagesize==1.2.0
|
||||||
@ -70,9 +70,9 @@ jinja2==2.11.3
|
|||||||
# mrkd
|
# mrkd
|
||||||
# sphinx
|
# sphinx
|
||||||
jsonschema==3.2.0
|
jsonschema==3.2.0
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
mako==1.1.5
|
mako==1.1.5
|
||||||
# via -r requirements.in
|
# via -r requirements.txt
|
||||||
markupsafe==2.0.1
|
markupsafe==2.0.1
|
||||||
# via
|
# via
|
||||||
# jinja2
|
# jinja2
|
||||||
@ -88,9 +88,9 @@ more-itertools==8.10.0
|
|||||||
# cheroot
|
# cheroot
|
||||||
# jaraco.functools
|
# jaraco.functools
|
||||||
mrkd==0.1.6
|
mrkd==0.1.6
|
||||||
# via -r requirements.in
|
# via -r requirements.txt
|
||||||
mypy==0.910
|
mypy==0.910
|
||||||
# via -r requirements.in
|
# via pyln.proto
|
||||||
mypy-extensions==0.4.3
|
mypy-extensions==0.4.3
|
||||||
# via mypy
|
# via mypy
|
||||||
packaging==21.0
|
packaging==21.0
|
||||||
@ -102,9 +102,9 @@ plac==1.3.3
|
|||||||
pluggy==0.13.1
|
pluggy==0.13.1
|
||||||
# via pytest
|
# via pytest
|
||||||
psutil==5.7.3
|
psutil==5.7.3
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
psycopg2-binary==2.8.6
|
psycopg2-binary==2.8.6
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
py==1.10.0
|
py==1.10.0
|
||||||
# via
|
# via
|
||||||
# pytest
|
# pytest
|
||||||
@ -112,9 +112,7 @@ py==1.10.0
|
|||||||
pycodestyle==2.5.0
|
pycodestyle==2.5.0
|
||||||
# via flake8
|
# via flake8
|
||||||
pycparser==2.20
|
pycparser==2.20
|
||||||
# via
|
# via cffi
|
||||||
# -r requirements.in
|
|
||||||
# cffi
|
|
||||||
pyflakes==2.1.1
|
pyflakes==2.1.1
|
||||||
# via flake8
|
# via flake8
|
||||||
pygments==2.10.0
|
pygments==2.10.0
|
||||||
@ -126,10 +124,10 @@ pyparsing==2.4.7
|
|||||||
pyrsistent==0.18.0
|
pyrsistent==0.18.0
|
||||||
# via jsonschema
|
# via jsonschema
|
||||||
pysocks==1.7.1
|
pysocks==1.7.1
|
||||||
# via -r requirements.in
|
# via pyln.proto
|
||||||
pytest==6.1.2
|
pytest==6.1.2
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# pyln-testing
|
||||||
# pytest-forked
|
# pytest-forked
|
||||||
# pytest-rerunfailures
|
# pytest-rerunfailures
|
||||||
# pytest-timeout
|
# pytest-timeout
|
||||||
@ -137,17 +135,17 @@ pytest==6.1.2
|
|||||||
pytest-forked==1.3.0
|
pytest-forked==1.3.0
|
||||||
# via pytest-xdist
|
# via pytest-xdist
|
||||||
pytest-rerunfailures==9.1.1
|
pytest-rerunfailures==9.1.1
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
pytest-timeout==1.4.2
|
pytest-timeout==1.4.2
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
pytest-xdist==2.2.1
|
pytest-xdist==2.2.1
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
python-bitcoinlib==0.11.0
|
python-bitcoinlib==0.11.0
|
||||||
# via -r requirements.in
|
# via pyln-testing
|
||||||
pytz==2021.1
|
pytz==2021.1
|
||||||
# via babel
|
# via babel
|
||||||
recommonmark==0.7.1
|
recommonmark==0.7.1
|
||||||
# via -r requirements.in
|
# via pyln-client
|
||||||
requests==2.26.0
|
requests==2.26.0
|
||||||
# via sphinx
|
# via sphinx
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
@ -171,13 +169,15 @@ sphinxcontrib-qthelp==1.0.3
|
|||||||
sphinxcontrib-serializinghtml==1.1.5
|
sphinxcontrib-serializinghtml==1.1.5
|
||||||
# via sphinx
|
# via sphinx
|
||||||
toml==0.10.2
|
toml==0.10.2
|
||||||
# via
|
# via pytest
|
||||||
# mypy
|
typed-ast==1.4.3
|
||||||
# pytest
|
# via mypy
|
||||||
typing-extensions==3.10.0.2
|
typing-extensions==3.10.0.2
|
||||||
# via mypy
|
# via mypy
|
||||||
urllib3==1.26.7
|
urllib3==1.26.7
|
||||||
# via requests
|
# via requests
|
||||||
|
websocket-client==1.2.1
|
||||||
|
# via -r requirements.txt
|
||||||
werkzeug==1.0.1
|
werkzeug==1.0.1
|
||||||
# via flask
|
# via flask
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
mrkd ~= 0.1.6
|
mrkd ~= 0.1.6
|
||||||
Mako ~= 1.1.3
|
Mako ~= 1.1.3
|
||||||
flake8 ~= 3.7.8
|
flake8 ~= 3.7.8
|
||||||
|
websocket-client
|
||||||
|
|
||||||
./contrib/pyln-client
|
./contrib/pyln-client
|
||||||
./contrib/pyln-proto
|
./contrib/pyln-proto
|
||||||
|
@ -2,7 +2,9 @@ from collections import namedtuple
|
|||||||
from fixtures import * # noqa: F401,F403
|
from fixtures import * # noqa: F401,F403
|
||||||
from fixtures import TEST_NETWORK
|
from fixtures import TEST_NETWORK
|
||||||
from flaky import flaky # noqa: F401
|
from flaky import flaky # noqa: F401
|
||||||
|
from ephemeral_port_reserve import reserve # type: ignore
|
||||||
from pyln.client import RpcError, Millisatoshi
|
from pyln.client import RpcError, Millisatoshi
|
||||||
|
import pyln.proto.wire as wire
|
||||||
from utils import (
|
from utils import (
|
||||||
only_one, wait_for, sync_blockheight, TIMEOUT,
|
only_one, wait_for, sync_blockheight, TIMEOUT,
|
||||||
expected_peer_features, expected_node_features,
|
expected_peer_features, expected_node_features,
|
||||||
@ -20,6 +22,7 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
import websocket
|
||||||
|
|
||||||
|
|
||||||
def test_connect(node_factory):
|
def test_connect(node_factory):
|
||||||
@ -3740,6 +3743,67 @@ def test_old_feerate(node_factory):
|
|||||||
l1.pay(l2, 1000)
|
l1.pay(l2, 1000)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.developer("needs --dev-allow-localhost")
|
||||||
|
def test_websocket(node_factory):
|
||||||
|
ws_port = reserve()
|
||||||
|
l1, l2 = node_factory.line_graph(2,
|
||||||
|
opts=[{'experimental-websocket-port': ws_port,
|
||||||
|
'dev-allow-localhost': None},
|
||||||
|
{'dev-allow-localhost': None}],
|
||||||
|
wait_for_announce=True)
|
||||||
|
assert l1.rpc.listconfigs()['experimental-websocket-port'] == ws_port
|
||||||
|
|
||||||
|
# Adapter to turn websocket into a stream "connection"
|
||||||
|
class BinWebSocket(object):
|
||||||
|
def __init__(self, hostname, port):
|
||||||
|
self.ws = websocket.WebSocket()
|
||||||
|
self.ws.connect("ws://" + hostname + ":" + str(port))
|
||||||
|
self.recvbuf = bytes()
|
||||||
|
|
||||||
|
def send(self, data):
|
||||||
|
self.ws.send(data, websocket.ABNF.OPCODE_BINARY)
|
||||||
|
|
||||||
|
def recv(self, maxlen):
|
||||||
|
while len(self.recvbuf) < maxlen:
|
||||||
|
self.recvbuf += self.ws.recv()
|
||||||
|
|
||||||
|
ret = self.recvbuf[:maxlen]
|
||||||
|
self.recvbuf = self.recvbuf[maxlen:]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
ws = BinWebSocket('localhost', ws_port)
|
||||||
|
lconn = wire.LightningConnection(ws,
|
||||||
|
wire.PublicKey(bytes.fromhex(l1.info['id'])),
|
||||||
|
wire.PrivateKey(bytes([1] * 32)),
|
||||||
|
is_initiator=True)
|
||||||
|
|
||||||
|
l1.daemon.wait_for_log('Websocket connection in from')
|
||||||
|
|
||||||
|
# Perform handshake.
|
||||||
|
lconn.shake()
|
||||||
|
|
||||||
|
# Expect to receive init msg.
|
||||||
|
msg = lconn.read_message()
|
||||||
|
assert int.from_bytes(msg[0:2], 'big') == 16
|
||||||
|
|
||||||
|
# Echo same message back.
|
||||||
|
lconn.send_message(msg)
|
||||||
|
|
||||||
|
# Now try sending a ping, ask for 50 bytes
|
||||||
|
msg = bytes((0, 18, 0, 50, 0, 0))
|
||||||
|
lconn.send_message(msg)
|
||||||
|
|
||||||
|
# Could actually reply with some gossip msg!
|
||||||
|
while True:
|
||||||
|
msg = lconn.read_message()
|
||||||
|
if int.from_bytes(msg[0:2], 'big') == 19:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Check node_announcement has websocket
|
||||||
|
assert (only_one(l2.rpc.listnodes(l1.info['id'])['nodes'])['addresses']
|
||||||
|
== [{'type': 'ipv4', 'address': '127.0.0.1', 'port': l1.port}, {'type': 'websocket', 'port': ws_port}])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.developer("dev-disconnect required")
|
@pytest.mark.developer("dev-disconnect required")
|
||||||
def test_ping_timeout(node_factory):
|
def test_ping_timeout(node_factory):
|
||||||
# Disconnects after this, but doesn't know it.
|
# Disconnects after this, but doesn't know it.
|
||||||
|
Loading…
Reference in New Issue
Block a user