From 39f00ee0911c573aa887d02ce483deeb8ae2b182 Mon Sep 17 00:00:00 2001 From: enaples Date: Tue, 24 Feb 2026 17:31:03 +0100 Subject: [PATCH 01/11] vls: backbone of new fixtures --- tests/fixtures.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ tests/vls.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/vls.py diff --git a/tests/fixtures.py b/tests/fixtures.py index 7ff7e26d4408..8ad2e4ca5e8f 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -11,6 +11,8 @@ import subprocess import tempfile import time +from pyln.testing.utils import env +from vls import ValidatingLightningSignerD @pytest.fixture @@ -25,6 +27,23 @@ def __init__(self, *args, **kwargs): kwargs["executable"] = "lightningd/lightningd" utils.LightningNode.__init__(self, *args, **kwargs) + # New VLS integration + mode = env("VLS_MODE", "cln:native") + + subdaemon = { + "cln:native": None, + "cln:socket": "path/to/vls/artifact" + }[mode] + + if subdaemon: + self.REQUEST = None + self.use_vlsd = self.subdaemon is not None + self.vlsd: ValidatingLightningSignerD | None = None + self.vls_dir = self.lightning_dir / "vlsd" + self.vlsd_port = utils.reserve_unused_port() + self.vlsd_rpc_port = utils.reserve_unused_port() + self.daemon.opts["subdaemon"] = subdaemon + # Avoid socket path name too long on Linux if os.uname()[0] == 'Linux' and \ len(str(self.lightning_dir / TEST_NETWORK / 'lightning-rpc')) >= 108: @@ -61,6 +80,32 @@ def __init__(self, *args, **kwargs): accts_db = self.db.provider.get_db('', 'accounts', 0) self.daemon.opts['bookkeeper-db'] = accts_db.get_dsn() + def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): + self.vls_dir.mkdir(exist_ok=True, parents=True) + + # We start the signer first, otherwise the lightningd startup hangs on the init message + if self.use_vlsd: + self.daemon.env["VLS_PORT"] = str(self.vlsd_port) + self.daemon.env["VLS_LSS"] = os.environ.get("LSS_URI", "") + self.vlsd = ValidatingLightningSignerD( + vlsd_dir=self.vls_dir, + vlsd_port=self.vlsd_port, + vlsd_rpc_port=self.vlsd_rpc_port, + node_id=self.node_id, + network=self.network, + ) + import threading + + threading.Timer(1, self.vlsd.start).start() + self.REQUEST.addfinalizer(self.vlsd.stop) + + self.start( + self, + wait_for_bitcoind_sync=wait_for_bitcoind_sync, + stderr_redir=stderr_redir, + ) + + class CompatLevel(object): """An object that encapsulates the compat-level of our build. diff --git a/tests/vls.py b/tests/vls.py new file mode 100644 index 000000000000..e4ab2461b7f5 --- /dev/null +++ b/tests/vls.py @@ -0,0 +1,42 @@ +from pyln.testing.utils import TailableProc, env +import logging +import os + +class ValidatingLightningSignerD(TailableProc): + def __init__(self, vlsd_dir, vlsd_port, vlsd_rpc_port, node_id, network): + TailableProc.__init__(self, vlsd_dir, verbose=True) + self.executable = env("REMOTE_SIGNER_CMD", 'vlsd2') + os.environ['ALLOWLIST'] = env( + 'REMOTE_SIGNER_ALLOWLIST', + 'contrib/remote_hsmd/TESTING_ALLOWLIST') + self.opts = [ + '--network={}'.format(network), + '--datadir={}'.format(vlsd_dir), + '--connect=http://localhost:{}'.format(vlsd_port), + '--rpc-server-port={}'.format(vlsd_rpc_port), + '--integration-test', + ] + self.prefix = 'vlsd2-%d' % (node_id) + self.vlsd_port = vlsd_port + + @property + def cmd_line(self): + return [self.executable] + self.opts + + def start(self, stdin=None, stdout_redir=True, stderr_redir=True, + wait_for_initialized=True): + TailableProc.start(self, stdin, stdout_redir, stderr_redir) + # We need to always wait for initialization + self.wait_for_log("vlsd2 git_desc") + logging.info("vlsd2 started") + + def stop(self, timeout=10): + logging.info("stopping vlsd2") + rc = TailableProc.stop(self, timeout) + logging.info("vlsd2 stopped") + self.logs_catchup() + return rc + + def __del__(self): + self.logs_catchup() + From 9fbb1e7646f78353c90a12952f8b240016ed3699 Mon Sep 17 00:00:00 2001 From: ScuttoZ Date: Wed, 25 Feb 2026 12:47:50 +0100 Subject: [PATCH 02/11] subdaemon path --- tests/fixtures.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 8ad2e4ca5e8f..f48a4c0aa11f 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -32,14 +32,15 @@ def __init__(self, *args, **kwargs): subdaemon = { "cln:native": None, - "cln:socket": "path/to/vls/artifact" + "cln:socket": f"hsmd:{self.lightning_dir/'validating-lightning-signer'/'target'/'debug'/'remote_hsmd_socket'}", }[mode] if subdaemon: self.REQUEST = None + self.subdaemon = subdaemon self.use_vlsd = self.subdaemon is not None self.vlsd: ValidatingLightningSignerD | None = None - self.vls_dir = self.lightning_dir / "vlsd" + self.vls_dir = self.lightning_dir / "validating-lightning-signer" self.vlsd_port = utils.reserve_unused_port() self.vlsd_rpc_port = utils.reserve_unused_port() self.daemon.opts["subdaemon"] = subdaemon @@ -99,12 +100,18 @@ def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): threading.Timer(1, self.vlsd.start).start() self.REQUEST.addfinalizer(self.vlsd.stop) - self.start( + utils.LightningNode.start( self, wait_for_bitcoind_sync=wait_for_bitcoind_sync, stderr_redir=stderr_redir, ) + def stop(self, timeout: int = 10): + utils.LightningNode.stop(self, timeout=timeout) + if self.vlsd is not None and self.use_vlsd: + rc = self.vlsd.stop(timeout=timeout) + print(f"VLSD2 exited with rc={rc}") + class CompatLevel(object): From 90cfffe83de8fd802464f2e6b5926a2dcb293245 Mon Sep 17 00:00:00 2001 From: enaples Date: Wed, 25 Feb 2026 16:03:08 +0100 Subject: [PATCH 03/11] vls: making vls class clone the latest version of vls repo --- tests/vls.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 13 deletions(-) diff --git a/tests/vls.py b/tests/vls.py index e4ab2461b7f5..354644f2a452 100644 --- a/tests/vls.py +++ b/tests/vls.py @@ -1,23 +1,137 @@ -from pyln.testing.utils import TailableProc, env +from pyln.testing.utils import TailableProc, env, reserve_unused_port import logging import os +import json +from pathlib import Path +from enum import Enum +from subprocess import run, PIPE +from typing import Union +import sys +import time + +__VERSION__ = "0.0.1" + +logging.basicConfig( + level=logging.INFO, + format='[%(asctime)s] %(levelname)s: %(message)s', + handlers=[logging.StreamHandler(stream=sys.stdout)], +) + +def chunk_string(string: str, size: int): + for i in range(0, len(string), size): + yield string[i: i + size] + + +def ratelimit_output(output: str): + sys.stdout.reconfigure(encoding='utf-8') + for i in chunk_string(output, 1024): + sys.stdout.write(i) + sys.stdout.flush() + time.sleep(0.01) + + +class Logger: + """Redirect logging output to a json object or stdout as appropriate.""" + def __init__(self, capture: bool = False): + self.json_output = {"result": [], + "log": []} + self.capture = capture + + def str_esc(self, raw_string: str) -> str: + assert isinstance(raw_string, str) + return json.dumps(raw_string)[1:-1] + + def debug(self, to_log: str): + assert isinstance(to_log, str) or hasattr(to_log, "__repr__") + if logging.root.level > logging.DEBUG: + return + if self.capture: + self.json_output['log'].append(self.str_esc(f"DEBUG: {to_log}")) + else: + logging.debug(to_log) + + def info(self, to_log: str): + assert isinstance(to_log, str) or hasattr(to_log, "__repr__") + if logging.root.level > logging.INFO: + return + if self.capture: + self.json_output['log'].append(self.str_esc(f"INFO: {to_log}")) + else: + print(to_log) + + def warning(self, to_log: str): + assert isinstance(to_log, str) or hasattr(to_log, "__repr__") + if logging.root.level > logging.WARNING: + return + if self.capture: + self.json_output['log'].append(self.str_esc(f"WARNING: {to_log}")) + else: + logging.warning(to_log) + + def error(self, to_log: str): + assert isinstance(to_log, str) or hasattr(to_log, "__repr__") + if logging.root.level > logging.ERROR: + return + if self.capture: + self.json_output['log'].append(self.str_esc(f"ERROR: {to_log}")) + else: + logging.error(to_log) + + def add_result(self, result: Union[str, None]): + assert json.dumps(result), "result must be json serializable" + self.json_output["result"].append(result) + + def reply_json(self): + """json output to stdout with accumulated result.""" + if len(log.json_output["result"]) == 1 and \ + isinstance(log.json_output["result"][0], list): + # unpack sources output + log.json_output["result"] = log.json_output["result"][0] + output = json.dumps(log.json_output, indent=3) + '\n' + ratelimit_output(output) + + +log = Logger() + +repos = ["https://gitlab.com/lightning-signer/validating-lightning-signer.git"] + class ValidatingLightningSignerD(TailableProc): - def __init__(self, vlsd_dir, vlsd_port, vlsd_rpc_port, node_id, network): - TailableProc.__init__(self, vlsd_dir, verbose=True) - self.executable = env("REMOTE_SIGNER_CMD", 'vlsd2') + def __init__(self, lightning_dir, node_id, network): + logging.info("Initializing ValidatingLightningSignerD") + log.info(f"Cloning repository into {lightning_dir}") + self.lightning_dir = lightning_dir + clone = run(['git', 'clone', repos[0]], cwd=self.lightning_dir, check=True, timeout=120) + signer_folder = repos[0].split("/")[-1].split(".git")[0] + vlsd_dir = Path(self.lightning_dir / signer_folder).resolve() + self.dir = vlsd_dir + self.port = reserve_unused_port() + self.rpc_port = reserve_unused_port() + + if clone.returncode != 0: + log.error(f"Failed to clone repository: {clone.stderr}") + else: + log.info(f"Successfully cloned repository: {clone.stdout}") + + cargo = run(['cargo', 'build'], cwd=self.dir, check=True, timeout=300) + if cargo.returncode != 0: + log.error(f"Failed to build vlsd: {cargo.stderr}") + else: + log.info("Successfully built vlsd") + + TailableProc.__init__(self, self.dir, verbose=True) + self.executable = env("REMOTE_SIGNER_CMD", Path(self.dir / "target" / "debug" / "vlsd")) os.environ['ALLOWLIST'] = env( 'REMOTE_SIGNER_ALLOWLIST', 'contrib/remote_hsmd/TESTING_ALLOWLIST') self.opts = [ '--network={}'.format(network), - '--datadir={}'.format(vlsd_dir), - '--connect=http://localhost:{}'.format(vlsd_port), - '--rpc-server-port={}'.format(vlsd_rpc_port), + '--datadir={}'.format(self.dir), + '--connect=http://localhost:{}'.format(self.port), + '--rpc-server-port={}'.format(self.rpc_port), '--integration-test', ] - self.prefix = 'vlsd2-%d' % (node_id) - self.vlsd_port = vlsd_port + self.prefix = 'vlsd-%d' % (node_id) @property def cmd_line(self): @@ -27,13 +141,13 @@ def start(self, stdin=None, stdout_redir=True, stderr_redir=True, wait_for_initialized=True): TailableProc.start(self, stdin, stdout_redir, stderr_redir) # We need to always wait for initialization - self.wait_for_log("vlsd2 git_desc") - logging.info("vlsd2 started") + self.wait_for_log("vlsd git_desc") + logging.info("vlsd started") def stop(self, timeout=10): - logging.info("stopping vlsd2") + logging.info("stopping vlsd") rc = TailableProc.stop(self, timeout) - logging.info("vlsd2 stopped") + logging.info("vlsd stopped") self.logs_catchup() return rc From 8a51f0108be94ae3e8076caa79ff9d2206a9691a Mon Sep 17 00:00:00 2001 From: enaples Date: Wed, 25 Feb 2026 16:10:29 +0100 Subject: [PATCH 04/11] fixtures: finally making LightingNode works with VLS --- tests/fixtures.py | 67 ++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index f48a4c0aa11f..4c6dc824d651 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -21,28 +21,25 @@ def node_cls(): class LightningNode(utils.LightningNode): - def __init__(self, *args, **kwargs): + def __init__(self, node_id, *args, **kwargs): # Yes, we really want to test the local development version, not # something in out path. kwargs["executable"] = "lightningd/lightningd" - utils.LightningNode.__init__(self, *args, **kwargs) + utils.LightningNode.__init__(self, node_id, *args, **kwargs) + + self.node_id = node_id + self.network = TEST_NETWORK # New VLS integration mode = env("VLS_MODE", "cln:native") - subdaemon = { "cln:native": None, "cln:socket": f"hsmd:{self.lightning_dir/'validating-lightning-signer'/'target'/'debug'/'remote_hsmd_socket'}", }[mode] + self.use_vlsd = subdaemon is not None + self.vlsd: ValidatingLightningSignerD | None = None if subdaemon: - self.REQUEST = None - self.subdaemon = subdaemon - self.use_vlsd = self.subdaemon is not None - self.vlsd: ValidatingLightningSignerD | None = None - self.vls_dir = self.lightning_dir / "validating-lightning-signer" - self.vlsd_port = utils.reserve_unused_port() - self.vlsd_rpc_port = utils.reserve_unused_port() self.daemon.opts["subdaemon"] = subdaemon # Avoid socket path name too long on Linux @@ -81,36 +78,30 @@ def __init__(self, *args, **kwargs): accts_db = self.db.provider.get_db('', 'accounts', 0) self.daemon.opts['bookkeeper-db'] = accts_db.get_dsn() - def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): - self.vls_dir.mkdir(exist_ok=True, parents=True) - - # We start the signer first, otherwise the lightningd startup hangs on the init message - if self.use_vlsd: - self.daemon.env["VLS_PORT"] = str(self.vlsd_port) - self.daemon.env["VLS_LSS"] = os.environ.get("LSS_URI", "") - self.vlsd = ValidatingLightningSignerD( - vlsd_dir=self.vls_dir, - vlsd_port=self.vlsd_port, - vlsd_rpc_port=self.vlsd_rpc_port, - node_id=self.node_id, - network=self.network, - ) - import threading - - threading.Timer(1, self.vlsd.start).start() - self.REQUEST.addfinalizer(self.vlsd.stop) - - utils.LightningNode.start( - self, - wait_for_bitcoind_sync=wait_for_bitcoind_sync, - stderr_redir=stderr_redir, + def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): + # We start the signer first, otherwise the lightningd startup hangs on the init message. + if self.use_vlsd: + self.vlsd = ValidatingLightningSignerD( + lightning_dir=self.lightning_dir, + node_id=self.node_id, + network=self.network, ) + self.daemon.env["VLS_PORT"] = str(self.vlsd.port) + self.daemon.env["VLS_LSS"] = os.environ.get("LSS_URI", "") + import threading + threading.Timer(1, self.vlsd.start).start() + + utils.LightningNode.start( + self, + wait_for_bitcoind_sync=wait_for_bitcoind_sync, + stderr_redir=stderr_redir, + ) - def stop(self, timeout: int = 10): - utils.LightningNode.stop(self, timeout=timeout) - if self.vlsd is not None and self.use_vlsd: - rc = self.vlsd.stop(timeout=timeout) - print(f"VLSD2 exited with rc={rc}") + def stop(self, timeout: int = 10): + utils.LightningNode.stop(self, timeout=timeout) + if self.vlsd is not None: + rc = self.vlsd.stop(timeout=timeout) + print(f"VLSD2 exited with rc={rc}") From c7db95529ba027d37228c774589beefd810e4dc3 Mon Sep 17 00:00:00 2001 From: enaples Date: Thu, 26 Feb 2026 10:33:44 +0100 Subject: [PATCH 05/11] vls: add `use_vls` option to fine-grained control on each instance instead of having an all-or-nothing logic for all nodes created by `node_factory` --- tests/fixtures.py | 53 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 4c6dc824d651..50544b7cf7a8 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,6 +1,7 @@ from utils import TEST_NETWORK, VALGRIND # noqa: F401,F403 -from pyln.testing.fixtures import directory, test_base_dir, test_name, chainparams, node_factory, bitcoind, teardown_checks, db_provider, executor, setup_logging, jsonschemas # noqa: F401,F403 +from pyln.testing.fixtures import directory, test_base_dir, test_name, chainparams, bitcoind, teardown_checks, db_provider, executor, setup_logging, jsonschemas # noqa: F401,F403 from pyln.testing import utils +from pyln.testing.utils import NodeFactory as _NodeFactory from utils import COMPAT from pathlib import Path @@ -15,13 +16,48 @@ from vls import ValidatingLightningSignerD +class NodeFactory(_NodeFactory): + """Make `use_vls` option reaches the `LightningNode.__init__` in + `NodeFactory` as node-level kwarg instead of being forwarded as a + lightningd CLI flag.""" + + def split_options(self, opts): + node_opts, cli_opts = super().split_options(opts) + if 'use_vls' in cli_opts: + node_opts['use_vls'] = cli_opts.pop('use_vls') + return node_opts, cli_opts + + @pytest.fixture def node_cls(): return LightningNode +# Override the default fixture to use the new `NodeFactory` which supports `use_vls` as a node-level option. +@pytest.fixture +def node_factory(request, directory, test_name, bitcoind, executor, db_provider, teardown_checks, node_cls, jsonschemas): + nf = NodeFactory( + request, + test_name, + bitcoind, + executor, + directory=directory, + db_provider=db_provider, + node_cls=node_cls, + jsonschemas=jsonschemas, + ) + + yield nf + ok, errs = nf.killall([not n.may_fail for n in nf.nodes]) + + for e in errs: + print(e.format()) + + if not ok: + raise Exception("At least one lightning exited with unexpected non-zero return code") + class LightningNode(utils.LightningNode): - def __init__(self, node_id, *args, **kwargs): + def __init__(self, node_id, *args, use_vls=None, **kwargs): # Yes, we really want to test the local development version, not # something in out path. kwargs["executable"] = "lightningd/lightningd" @@ -30,12 +66,19 @@ def __init__(self, node_id, *args, **kwargs): self.node_id = node_id self.network = TEST_NETWORK - # New VLS integration - mode = env("VLS_MODE", "cln:native") + if use_vls is True: + self.vls_mode = "cln:socket" + elif use_vls is False: + self.vls_mode = "cln:native" + else: + # use_vls=None (default) falls back to the VLS_MODE env var. + # Setting this env var causes all nodes use the same mode + self.vls_mode = env("VLS_MODE", "cln:native") + subdaemon = { "cln:native": None, "cln:socket": f"hsmd:{self.lightning_dir/'validating-lightning-signer'/'target'/'debug'/'remote_hsmd_socket'}", - }[mode] + }[self.vls_mode] self.use_vlsd = subdaemon is not None self.vlsd: ValidatingLightningSignerD | None = None From b62be7967d323682b1086fb7876ca4ab7196a318 Mon Sep 17 00:00:00 2001 From: enaples Date: Thu, 26 Feb 2026 10:52:34 +0100 Subject: [PATCH 06/11] tests: simple vls test --- tests/test_pay.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index f73262c437da..4b17cb63a511 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -22,6 +22,18 @@ import unittest +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +def test_vls_simple(node_factory): + l1, l2 = node_factory.line_graph(2, opts={'use_vls': True}) + + inv = l2.rpc.invoice(123000, 'test_vls_simple', 'description')['bolt11'] + details = l1.dev_pay(inv, dev_use_shadow=False) + assert details['status'] == 'complete' + assert details['amount_msat'] == Millisatoshi(123000) + assert details['destination'] == l2.info['id'] + + @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_pay(node_factory): From 9451e5d4c063b4d68c87e52212aae654dbc3c954 Mon Sep 17 00:00:00 2001 From: enaples Date: Thu, 26 Feb 2026 12:31:19 +0100 Subject: [PATCH 07/11] vls: making improvements on vls integration hard-conding the socket binary path before instantiating the vls class instance is not ideal --- tests/fixtures.py | 12 +++--------- tests/vls.py | 3 ++- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 50544b7cf7a8..7f05e3c6af8e 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -75,15 +75,8 @@ def __init__(self, node_id, *args, use_vls=None, **kwargs): # Setting this env var causes all nodes use the same mode self.vls_mode = env("VLS_MODE", "cln:native") - subdaemon = { - "cln:native": None, - "cln:socket": f"hsmd:{self.lightning_dir/'validating-lightning-signer'/'target'/'debug'/'remote_hsmd_socket'}", - }[self.vls_mode] - - self.use_vlsd = subdaemon is not None + self.use_vls = use_vls is not None self.vlsd: ValidatingLightningSignerD | None = None - if subdaemon: - self.daemon.opts["subdaemon"] = subdaemon # Avoid socket path name too long on Linux if os.uname()[0] == 'Linux' and \ @@ -123,12 +116,13 @@ def __init__(self, node_id, *args, use_vls=None, **kwargs): def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): # We start the signer first, otherwise the lightningd startup hangs on the init message. - if self.use_vlsd: + if self.use_vls: self.vlsd = ValidatingLightningSignerD( lightning_dir=self.lightning_dir, node_id=self.node_id, network=self.network, ) + self.daemon.opts["subdaemon"] = f"hsmd:{self.vlsd.remote_socket}" self.daemon.env["VLS_PORT"] = str(self.vlsd.port) self.daemon.env["VLS_LSS"] = os.environ.get("LSS_URI", "") import threading diff --git a/tests/vls.py b/tests/vls.py index 354644f2a452..0ef5fddf90de 100644 --- a/tests/vls.py +++ b/tests/vls.py @@ -120,7 +120,8 @@ def __init__(self, lightning_dir, node_id, network): log.info("Successfully built vlsd") TailableProc.__init__(self, self.dir, verbose=True) - self.executable = env("REMOTE_SIGNER_CMD", Path(self.dir / "target" / "debug" / "vlsd")) + self.executable = env("REMOTE_SIGNER_CMD", Path(self.dir / "target" / "debug" / "vlsd").resolve()) + self.remote_socket = Path(self.dir / "target" / "debug" / "remote_hsmd_socket").resolve() os.environ['ALLOWLIST'] = env( 'REMOTE_SIGNER_ALLOWLIST', 'contrib/remote_hsmd/TESTING_ALLOWLIST') From c1dc50689e18e3262ca9c27ed66ce8ba8bec3883 Mon Sep 17 00:00:00 2001 From: enaples Date: Thu, 26 Feb 2026 12:48:18 +0100 Subject: [PATCH 08/11] vls: fixing the executable as str after resolving path since `cmd_line` wants a string --- tests/vls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/vls.py b/tests/vls.py index 0ef5fddf90de..3f8631290b17 100644 --- a/tests/vls.py +++ b/tests/vls.py @@ -120,7 +120,7 @@ def __init__(self, lightning_dir, node_id, network): log.info("Successfully built vlsd") TailableProc.__init__(self, self.dir, verbose=True) - self.executable = env("REMOTE_SIGNER_CMD", Path(self.dir / "target" / "debug" / "vlsd").resolve()) + self.executable = env("REMOTE_SIGNER_CMD", str(Path(self.dir / "target" / "debug" / "vlsd").resolve())) self.remote_socket = Path(self.dir / "target" / "debug" / "remote_hsmd_socket").resolve() os.environ['ALLOWLIST'] = env( 'REMOTE_SIGNER_ALLOWLIST', From 3f7725610b65294a63214c5e6646cb810dd19b80 Mon Sep 17 00:00:00 2001 From: enaples Date: Wed, 4 Mar 2026 11:15:27 +0100 Subject: [PATCH 09/11] tests: fixing build and env var to make vls test pass --- tests/fixtures.py | 5 +++++ tests/test_pay.py | 2 +- tests/vls.py | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 7f05e3c6af8e..977b1597e275 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -123,8 +123,13 @@ def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): network=self.network, ) self.daemon.opts["subdaemon"] = f"hsmd:{self.vlsd.remote_socket}" + # FIXME: we should not need to set these env vars, + # but remote_hsmd_socket CLI options. self.daemon.env["VLS_PORT"] = str(self.vlsd.port) self.daemon.env["VLS_LSS"] = os.environ.get("LSS_URI", "") + self.daemon.env["VLS_NETWORK"] = self.network + self.daemon.env["BITCOIND_RPC_URL"] = env("BITCOIND_RPC_URL", "http://rpcuser:rpcpass@127.0.0.1:{}".format(self.bitcoin.rpcport)) + self.daemon.env["VLS_CLN_VERSION"] = env("VLS_CLN_VERSION", "v25.12-391-gc1dc506-modded") import threading threading.Timer(1, self.vlsd.start).start() diff --git a/tests/test_pay.py b/tests/test_pay.py index 4b17cb63a511..850b82fc08c4 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -25,7 +25,7 @@ @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_vls_simple(node_factory): - l1, l2 = node_factory.line_graph(2, opts={'use_vls': True}) + l1, l2 = node_factory.line_graph(2, opts=[{'use_vls': True}, {}]) inv = l2.rpc.invoice(123000, 'test_vls_simple', 'description')['bolt11'] details = l1.dev_pay(inv, dev_use_shadow=False) diff --git a/tests/vls.py b/tests/vls.py index 3f8631290b17..7a8c1ead058e 100644 --- a/tests/vls.py +++ b/tests/vls.py @@ -113,7 +113,7 @@ def __init__(self, lightning_dir, node_id, network): else: log.info(f"Successfully cloned repository: {clone.stdout}") - cargo = run(['cargo', 'build'], cwd=self.dir, check=True, timeout=300) + cargo = run(['cargo', 'build', '--features', 'developer'], cwd=self.dir, check=True, timeout=300) if cargo.returncode != 0: log.error(f"Failed to build vlsd: {cargo.stderr}") else: @@ -130,7 +130,7 @@ def __init__(self, lightning_dir, node_id, network): '--datadir={}'.format(self.dir), '--connect=http://localhost:{}'.format(self.port), '--rpc-server-port={}'.format(self.rpc_port), - '--integration-test', + # '--integration-test', ] self.prefix = 'vlsd-%d' % (node_id) From 1723d9fbef1bee0c3e4e8c8839a1e4837b16ed68 Mon Sep 17 00:00:00 2001 From: enaples Date: Wed, 4 Mar 2026 11:48:54 +0100 Subject: [PATCH 10/11] tests: making more general bicoind connection --- tests/fixtures.py | 7 ++++--- tests/utils.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index 977b1597e275..f5262c6201db 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,4 +1,4 @@ -from utils import TEST_NETWORK, VALGRIND # noqa: F401,F403 +from utils import TEST_NETWORK, BITCOIND_CONFIG, VALGRIND # noqa: F401,F403 from pyln.testing.fixtures import directory, test_base_dir, test_name, chainparams, bitcoind, teardown_checks, db_provider, executor, setup_logging, jsonschemas # noqa: F401,F403 from pyln.testing import utils from pyln.testing.utils import NodeFactory as _NodeFactory @@ -128,8 +128,9 @@ def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): self.daemon.env["VLS_PORT"] = str(self.vlsd.port) self.daemon.env["VLS_LSS"] = os.environ.get("LSS_URI", "") self.daemon.env["VLS_NETWORK"] = self.network - self.daemon.env["BITCOIND_RPC_URL"] = env("BITCOIND_RPC_URL", "http://rpcuser:rpcpass@127.0.0.1:{}".format(self.bitcoin.rpcport)) - self.daemon.env["VLS_CLN_VERSION"] = env("VLS_CLN_VERSION", "v25.12-391-gc1dc506-modded") + self.daemon.env["BITCOIND_RPC_URL"] = env("BITCOIND_RPC_URL", f"http://{BITCOIND_CONFIG['rpcuser']}:{BITCOIND_CONFIG['rpcpassword']}@127.0.0.1:{self.bitcoin.rpcport}") + cln_version_str = subprocess.check_output([self.daemon.executable, "--version"]).decode('ascii').strip() + self.daemon.env["VLS_CLN_VERSION"] = env("VLS_CLN_VERSION", cln_version_str) import threading threading.Timer(1, self.vlsd.start).start() diff --git a/tests/utils.py b/tests/utils.py index 9fc902aac23b..99c38e8d2d29 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,7 +2,7 @@ from pyln.testing.utils import env, only_one, wait_for, write_config, TailableProc, sync_blockheight, wait_channel_quiescent, get_tx_p2wsh_outnum, mine_funding_to_announce, scid_to_int # noqa: F401 import bitstring from pyln.client import Millisatoshi -from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, EXPERIMENTAL_SPLICING +from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, EXPERIMENTAL_SPLICING, BITCOIND_CONFIG from pyln.proto.onion import TlvPayload import struct import subprocess From be7c2fe79b49e561e9d8227210c92f516edbdd86 Mon Sep 17 00:00:00 2001 From: enaples Date: Wed, 4 Mar 2026 12:04:28 +0100 Subject: [PATCH 11/11] tests: making getting envars on vls class consistent --- tests/fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index f5262c6201db..2943e997b756 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -126,8 +126,8 @@ def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): # FIXME: we should not need to set these env vars, # but remote_hsmd_socket CLI options. self.daemon.env["VLS_PORT"] = str(self.vlsd.port) - self.daemon.env["VLS_LSS"] = os.environ.get("LSS_URI", "") - self.daemon.env["VLS_NETWORK"] = self.network + self.daemon.env["VLS_LSS"] = env("LSS_URI", "") + self.daemon.env["VLS_NETWORK"] = env("VLS_NETWORK", self.network) self.daemon.env["BITCOIND_RPC_URL"] = env("BITCOIND_RPC_URL", f"http://{BITCOIND_CONFIG['rpcuser']}:{BITCOIND_CONFIG['rpcpassword']}@127.0.0.1:{self.bitcoin.rpcport}") cln_version_str = subprocess.check_output([self.daemon.executable, "--version"]).decode('ascii').strip() self.daemon.env["VLS_CLN_VERSION"] = env("VLS_CLN_VERSION", cln_version_str)