Skip to content

Commit

Permalink
experimental-websocket: option to enable autodetection of WebSocket t…
Browse files Browse the repository at this point in the history
…ransport.

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell committed Aug 23, 2021
1 parent 2ef3263 commit c896708
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 9 deletions.
4 changes: 3 additions & 1 deletion common/features.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ static const struct feature_style feature_styles[] = {
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
[CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } },
{ OPT_WEBSOCKET,
.copy_style = { [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } },
};

struct dependency {
Expand Down Expand Up @@ -399,7 +401,7 @@ static const char *feature_name(const tal_t *ctx, size_t f)
NULL,
"option_onion_messages", /* 38/39 */
NULL, /* 40/41 */
NULL,
"option_websocket",
NULL,
NULL,
NULL,
Expand Down
6 changes: 6 additions & 0 deletions common/features.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,10 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits);

#define OPT_SHUTDOWN_WRONG_FUNDING 104

/* BOLT-websocket #9:
*
* | 42/43 | `option_websocket` |... N ...
*/
#define OPT_WEBSOCKET 42

#endif /* LIGHTNING_COMMON_FEATURES_H */
6 changes: 4 additions & 2 deletions doc/lightning-listconfigs.7

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions doc/lightning-listconfigs.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-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-websocket** (boolean, optional): `experimental-websocket` field from config or cmdline, or default
- **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
- **pid-file** (string, optional): `pid-file` field from config or cmdline, or default
Expand Down Expand Up @@ -84,7 +85,7 @@ On success, an object is returned, containing:
- **log-prefix** (string, optional): `log-prefix` field from config or cmdline, or default
- **log-file** (string, optional): `log-file` field from config or cmdline, or default
- **log-timestamps** (boolean, optional): `log-timestamps` field from config or cmdline, or default
- **force-feerates** (string, optional): `force-feerates` field from config or cmdline, if any
- **force-feerates** (string, optional): force-feerate configuration setting, if any
- **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one)
- **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any
[comment]: # (GENERATE-FROM-SCHEMA-END)
Expand Down Expand Up @@ -204,4 +205,4 @@ RESOURCES
---------

Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:ad98179a7b6254a936d4fde179918b6a975e186adcbc396917a0c2ed2888519e)
[comment]: # ( SHA256STAMP:6083722fadd8b5724326fc2685f05b5d056c0a6ab0752af64b10294173d1f9a2)
12 changes: 9 additions & 3 deletions doc/lightningd-config.5
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ What log level to print out: options are io, debug, info, unusual,
broken\. If \fISUBSYSTEM\fR is supplied, this sets the logging level
for any subsystem containing that string\. Subsystems include:


.RS
.IP \[bu]
\fIlightningd\fR: The main lightning daemon
Expand All @@ -171,7 +170,6 @@ for any subsystem containing that string\. Subsystems include:
The following subsystems exist for each channel, where N is an incrementing
internal integer id assigned for the lifetime of the channel:


.RS
.IP \[bu]
\fIopeningd-chan#N\fR: Each opening / idling daemon
Expand Down Expand Up @@ -627,6 +625,14 @@ about whether to add funds or not to a proposed channel is handled
automatically by a plugin that implements the appropriate logic for
your needs\. The default behavior is to not contribute funds\.


\fBexperimental-websocket\fR


Specifying this enables support for accepting incoming WebSocket
connections: the normal protocol is expected to be sent over
WebSocket binary frames once the connection is upgraded\.

.SH BUGS

You should report bugs on our github issues page, and maybe submit a fix
Expand All @@ -652,4 +658,4 @@ Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
Note: the modules in the ccan/ directory have their own licenses, but
the rest of the code is covered by the BSD-style MIT license\.

\" SHA256STAMP:1c392f3fee66dc6c1fc2c34200204a9be1d79e53fd5fb1720ad169fc671f71c0
\" SHA256STAMP:114b4e458af6112500f2f01210760f1577f9e82fc74e03b6d821f669287c93b2
6 changes: 6 additions & 0 deletions doc/lightningd-config.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,12 @@ about whether to add funds or not to a proposed channel is handled
automatically by a plugin that implements the appropriate logic for
your needs. The default behavior is to not contribute funds.

**experimental-websocket**

Specifying this enables support for accepting incoming WebSocket
connections: the normal protocol is expected to be sent over
WebSocket binary frames once the connection is upgraded.

BUGS
----

Expand Down
4 changes: 4 additions & 0 deletions doc/schemas/listconfigs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@
"type": "boolean",
"description": "`experimental-shutdown-wrong-funding` field from config or cmdline, or default"
},
"experimental-websocket": {
"type": "boolean",
"description": "`experimental-websocket` field from config or cmdline, or default"
},
"rgb": {
"type": "hex",
"description": "`rgb` field from config or cmdline, or default",
Expand Down
9 changes: 8 additions & 1 deletion lightningd/connect_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,14 @@ int connectd_init(struct lightningd *ld)
int hsmfd;
struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr;
enum addr_listen_announce *listen_announce = ld->proposed_listen_announce;
const char *websocket_helper_path = NULL;
const char *websocket_helper_path;

if (feature_offered(ld->our_features->bits[NODE_ANNOUNCE_FEATURE],
OPT_WEBSOCKET))
websocket_helper_path = subdaemon_path(tmpctx, ld,
"lightning_websocketd");
else
websocket_helper_path = NULL;

if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
fatal("Could not socketpair for connectd<->gossipd");
Expand Down
17 changes: 17 additions & 0 deletions lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,14 @@ static char *opt_set_wumbo(struct lightningd *ld)
return NULL;
}

static char *opt_set_websocket(struct lightningd *ld)
{
feature_set_or(ld->our_features,
take(feature_set_for_feature(NULL,
OPTIONAL_FEATURE(OPT_WEBSOCKET))));
return NULL;
}

static char *opt_set_dual_fund(struct lightningd *ld)
{
/* Dual funding implies anchor outputs */
Expand Down Expand Up @@ -942,6 +950,10 @@ static void register_opts(struct lightningd *ld)
" and allow peers to establish channels"
" via v2 channel open protocol.");

opt_register_early_noarg("--experimental-websocket",
opt_set_websocket, ld,
"experimental: allow peers to connect using"
" WebSockets (RFC6455)");
/* This affects our features, so set early. */
opt_register_early_noarg("--experimental-onion-messages",
opt_set_onion_messages, ld,
Expand Down Expand Up @@ -1386,6 +1398,11 @@ static void add_config(struct lightningd *ld,
feature_offered(ld->our_features
->bits[INIT_FEATURE],
OPT_DUAL_FUND));
} else if (opt->cb == (void *)opt_set_websocket) {
json_add_bool(response, name0,
feature_offered(ld->our_features
->bits[NODE_ANNOUNCE_FEATURE],
OPT_WEBSOCKET));
} else if (opt->cb == (void *)opt_set_onion_messages) {
json_add_bool(response, name0,
feature_offered(ld->our_features
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mrkd ~= 0.1.6
Mako ~= 1.1.3
flake8 ~= 3.7.8
websocket-client

./contrib/pyln-client
./contrib/pyln-proto
Expand Down
50 changes: 50 additions & 0 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from fixtures import TEST_NETWORK
from flaky import flaky # noqa: F401
from pyln.client import RpcError, Millisatoshi
import pyln.proto.wire as wire
from utils import (
only_one, wait_for, sync_blockheight, TIMEOUT,
expected_peer_features, expected_node_features,
Expand All @@ -20,6 +21,7 @@
import shutil
import time
import unittest
import websocket


def test_connect(node_factory):
Expand Down Expand Up @@ -3734,3 +3736,51 @@ def test_old_feerate(node_factory):

# This will timeout if l2 didn't accept fee.
l1.pay(l2, 1000)


def test_websocket(node_factory):
l1 = node_factory.get_node(options={'experimental-websocket': None, 'log-level': 'io'})
assert l1.rpc.listconfigs()['experimental-websocket']

# 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', l1.port)
lconn = wire.LightningConnection(ws,
wire.PublicKey(bytes.fromhex(l1.info['id'])),
wire.PrivateKey(bytes([1] * 32)),
is_initiator=True)
# 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

0 comments on commit c896708

Please sign in to comment.