|
2 | 2 | from fixtures import * # noqa: F401,F403
|
3 | 3 | from fixtures import TEST_NETWORK
|
4 | 4 | from flaky import flaky # noqa: F401
|
| 5 | +from ephemeral_port_reserve import reserve # type: ignore |
5 | 6 | from pyln.client import RpcError, Millisatoshi
|
| 7 | +import pyln.proto.wire as wire |
6 | 8 | from utils import (
|
7 | 9 | only_one, wait_for, sync_blockheight, TIMEOUT,
|
8 | 10 | expected_peer_features, expected_node_features,
|
|
20 | 22 | import shutil
|
21 | 23 | import time
|
22 | 24 | import unittest
|
| 25 | +import websocket |
23 | 26 |
|
24 | 27 |
|
25 | 28 | def test_connect(node_factory):
|
@@ -3740,6 +3743,67 @@ def test_old_feerate(node_factory):
|
3740 | 3743 | l1.pay(l2, 1000)
|
3741 | 3744 |
|
3742 | 3745 |
|
| 3746 | +@pytest.mark.developer("needs --dev-allow-localhost") |
| 3747 | +def test_websocket(node_factory): |
| 3748 | + ws_port = reserve() |
| 3749 | + l1, l2 = node_factory.line_graph(2, |
| 3750 | + opts=[{'experimental-websocket-port': ws_port, |
| 3751 | + 'dev-allow-localhost': None}, |
| 3752 | + {'dev-allow-localhost': None}], |
| 3753 | + wait_for_announce=True) |
| 3754 | + assert l1.rpc.listconfigs()['experimental-websocket-port'] == ws_port |
| 3755 | + |
| 3756 | + # Adapter to turn websocket into a stream "connection" |
| 3757 | + class BinWebSocket(object): |
| 3758 | + def __init__(self, hostname, port): |
| 3759 | + self.ws = websocket.WebSocket() |
| 3760 | + self.ws.connect("ws://" + hostname + ":" + str(port)) |
| 3761 | + self.recvbuf = bytes() |
| 3762 | + |
| 3763 | + def send(self, data): |
| 3764 | + self.ws.send(data, websocket.ABNF.OPCODE_BINARY) |
| 3765 | + |
| 3766 | + def recv(self, maxlen): |
| 3767 | + while len(self.recvbuf) < maxlen: |
| 3768 | + self.recvbuf += self.ws.recv() |
| 3769 | + |
| 3770 | + ret = self.recvbuf[:maxlen] |
| 3771 | + self.recvbuf = self.recvbuf[maxlen:] |
| 3772 | + return ret |
| 3773 | + |
| 3774 | + ws = BinWebSocket('localhost', ws_port) |
| 3775 | + lconn = wire.LightningConnection(ws, |
| 3776 | + wire.PublicKey(bytes.fromhex(l1.info['id'])), |
| 3777 | + wire.PrivateKey(bytes([1] * 32)), |
| 3778 | + is_initiator=True) |
| 3779 | + |
| 3780 | + l1.daemon.wait_for_log('Websocket connection in from') |
| 3781 | + |
| 3782 | + # Perform handshake. |
| 3783 | + lconn.shake() |
| 3784 | + |
| 3785 | + # Expect to receive init msg. |
| 3786 | + msg = lconn.read_message() |
| 3787 | + assert int.from_bytes(msg[0:2], 'big') == 16 |
| 3788 | + |
| 3789 | + # Echo same message back. |
| 3790 | + lconn.send_message(msg) |
| 3791 | + |
| 3792 | + # Now try sending a ping, ask for 50 bytes |
| 3793 | + msg = bytes((0, 18, 0, 50, 0, 0)) |
| 3794 | + lconn.send_message(msg) |
| 3795 | + |
| 3796 | + # Could actually reply with some gossip msg! |
| 3797 | + while True: |
| 3798 | + msg = lconn.read_message() |
| 3799 | + if int.from_bytes(msg[0:2], 'big') == 19: |
| 3800 | + break |
| 3801 | + |
| 3802 | + # Check node_announcement has websocket |
| 3803 | + assert (only_one(l2.rpc.listnodes(l1.info['id'])['nodes'])['addresses'] |
| 3804 | + == [{'type': 'ipv4', 'address': '127.0.0.1', 'port': l1.port}, {'type': 'websocket', 'port': ws_port}]) |
| 3805 | + |
| 3806 | + |
3743 | 3807 | @pytest.mark.developer("dev-disconnect required")
|
3744 | 3808 | def test_ping_timeout(node_factory):
|
3745 | 3809 | # Disconnects after this, but doesn't know it.
|
|
0 commit comments