Files
palladum-lightning/tests/test_cln_lsps.py
Peter Neuroth 9e301f0f9c lsp_plugin: rename cmds and opts to fit convention
We use `experimental-*` for documented commands instead of `dev-` which
are undocumented commands.

Signed-off-by: Peter Neuroth <pet.v.ne@gmail.com>
2025-11-13 10:58:49 +10:30

255 lines
7.3 KiB
Python

from fixtures import * # noqa: F401,F403
from pyln.testing.utils import RUST
from utils import only_one
import os
import pytest
import unittest
RUST_PROFILE = os.environ.get("RUST_PROFILE", "debug")
def test_lsps_service_disabled(node_factory):
"""By default we disable the LSPS service plugin.
It should only be enabled if we explicitly set the config option
`lsps-service=True`.
"""
l1 = node_factory.get_node(1)
l1.daemon.is_in_log("`lsps-service` not enabled")
@unittest.skipUnless(RUST, 'RUST is not enabled')
def test_lsps0_listprotocols(node_factory):
l1, l2 = node_factory.get_nodes(
2,
opts=[
{"experimental-lsps-client": None},
{
"experimental-lsps2-service": None,
"experimental-lsps2-promise-secret": "0" * 64,
},
],
)
# We don't need a channel to query for lsps services
node_factory.join_nodes([l1, l2], fundchannel=False)
res = l1.rpc.lsps_listprotocols(lsp_id=l2.info["id"])
assert res
def test_lsps2_enabled(node_factory):
l1, l2 = node_factory.get_nodes(
2,
opts=[
{"experimental-lsps-client": None},
{
"experimental-lsps2-service": None,
"experimental-lsps2-promise-secret": "0" * 64,
},
],
)
node_factory.join_nodes([l1, l2], fundchannel=False)
res = l1.rpc.lsps_listprotocols(lsp_id=l2.info["id"])
assert res["protocols"] == [2]
def test_lsps2_getinfo(node_factory):
plugin = os.path.join(os.path.dirname(__file__), "plugins/lsps2_policy.py")
l1, l2 = node_factory.get_nodes(
2,
opts=[
{"experimental-lsps-client": None},
{
"experimental-lsps2-service": None,
"experimental-lsps2-promise-secret": "0" * 64,
"plugin": plugin,
},
]
)
node_factory.join_nodes([l1, l2], fundchannel=False)
res = l1.rpc.lsps_lsps2_getinfo(lsp_id=l2.info["id"])
assert res["opening_fee_params_menu"]
def test_lsps2_buy(node_factory):
# We need a policy service to fetch from.
plugin = os.path.join(os.path.dirname(__file__), "plugins/lsps2_policy.py")
l1, l2 = node_factory.get_nodes(
2,
opts=[
{"experimental-lsps-client": None},
{
"experimental-lsps2-service": None,
"experimental-lsps2-promise-secret": "0" * 64,
"plugin": plugin,
},
]
)
# We don't need a channel to query for lsps services
node_factory.join_nodes([l1, l2], fundchannel=False)
res = l1.rpc.lsps_lsps2_getinfo(lsp_id=l2.info["id"])
params = res["opening_fee_params_menu"][0]
res = l1.rpc.lsps_lsps2_buy(lsp_id=l2.info["id"], opening_fee_params=params)
assert res
def test_lsps2_buyjitchannel_no_mpp_var_invoice(node_factory, bitcoind):
"""Tests the creation of a "Just-In-Time-Channel" (jit-channel).
At the beginning we have the following situation where l2 acts as the LSP
(LSP)
l1 l2----l3
l1 now wants to get a channel from l2 via the lsps2 jit-channel protocol:
- l1 requests a new jit channel form l2
- l1 creates an invoice based on the opening fee parameters it got from l2
- l3 pays the invoice
- l2 opens a channel to l1 and forwards the payment (deducted by a fee)
eventualy this will result in the following situation
(LSP)
l1----l2----l3
"""
# We need a policy service to fetch from.
plugin = os.path.join(os.path.dirname(__file__), "plugins/lsps2_policy.py")
l1, l2, l3 = node_factory.get_nodes(
3,
opts=[
{"experimental-lsps-client": None},
{
"experimental-lsps2-service": None,
"experimental-lsps2-promise-secret": "0" * 64,
"plugin": plugin,
"fee-base": 0, # We are going to deduct our fee anyways,
"fee-per-satoshi": 0, # We are going to deduct our fee anyways,
},
{},
],
)
# Give the LSP some funds to open jit-channels
addr = l2.rpc.newaddr()["bech32"]
bitcoind.rpc.sendtoaddress(addr, 1)
bitcoind.generate_block(1)
node_factory.join_nodes([l3, l2], fundchannel=True, wait_for_announce=True)
node_factory.join_nodes([l1, l2], fundchannel=False)
chanid = only_one(l3.rpc.listpeerchannels(l2.info["id"])["channels"])[
"short_channel_id"
]
inv = l1.rpc.lsps_jitchannel(
lsp_id=l2.info["id"],
amount_msat="any",
description="lsp-jit-channel-0",
label="lsp-jit-channel-0",
)
assert inv
dec = l3.rpc.decode(inv["bolt11"])
assert dec
routehint = only_one(only_one(dec["routes"]))
amt = 10000000
route = [
{"amount_msat": amt, "id": l2.info["id"], "delay": 14, "channel": chanid},
{
"amount_msat": amt,
"id": l1.info["id"],
"delay": 8,
"channel": routehint["short_channel_id"],
},
]
l3.rpc.sendpay(
route,
dec["payment_hash"],
payment_secret=inv["payment_secret"],
bolt11=inv["bolt11"],
partid=0,
)
res = l3.rpc.waitsendpay(dec["payment_hash"])
assert res["payment_preimage"]
# l1 should have gotten a jit-channel.
chs = l1.rpc.listpeerchannels()["channels"]
assert len(chs) == 1
def test_lsps2_non_approved_zero_conf(node_factory, bitcoind):
"""Checks that we don't allow zerof_conf channels from an LSP if we did
not approve it first.
"""
# We need a policy service to fetch from.
plugin = os.path.join(os.path.dirname(__file__), "plugins/lsps2_policy.py")
l1, l2, l3 = node_factory.get_nodes(
3,
opts=[
{"experimental-lsps-client": None},
{
"experimental-lsps2-service": None,
"experimental-lsps2-promise-secret": "0" * 64,
"plugin": plugin,
"fee-base": 0, # We are going to deduct our fee anyways,
"fee-per-satoshi": 0, # We are going to deduct our fee anyways,
},
{"disable-mpp": None},
],
)
# Give the LSP some funds to open jit-channels
addr = l2.rpc.newaddr()["bech32"]
bitcoind.rpc.sendtoaddress(addr, 1)
bitcoind.generate_block(1)
node_factory.join_nodes([l3, l2], fundchannel=True, wait_for_announce=True)
node_factory.join_nodes([l1, l2], fundchannel=False)
fee_opt = l1.rpc.lsps_lsps2_getinfo(lsp_id=l2.info["id"])[
"opening_fee_params_menu"
][0]
buy_res = l1.rpc.lsps_lsps2_buy(lsp_id=l2.info["id"], opening_fee_params=fee_opt)
hint = [
[
{
"id": l2.info["id"],
"short_channel_id": buy_res["jit_channel_scid"],
"fee_base_msat": 0,
"fee_proportional_millionths": 0,
"cltv_expiry_delta": buy_res["lsp_cltv_expiry_delta"],
}
]
]
bolt11 = l1.dev_invoice(
amount_msat="any",
description="lsp-invoice-1",
label="lsp-invoice-1",
dev_routes=hint,
)["bolt11"]
with pytest.raises(ValueError):
l3.rpc.pay(bolt11, amount_msat=10000000)
# l1 shouldn't have a new channel.
chs = l1.rpc.listpeerchannels()["channels"]
assert len(chs) == 0