From dbbb11312300f626f211e53c012bcc8e710151a4 Mon Sep 17 00:00:00 2001 From: Peter Neuroth Date: Wed, 30 Jul 2025 16:29:26 +0200 Subject: [PATCH] tests: Add tests for extra_tlvs in hook Adds some testcases for custom tlvs, set by a htlc_accepted_hook. We check that the custom tlvs replace the update_add_htlc_tlvs and get forwarded to the peer. We also check that a malformed tlv will result in a **BROKEN** behaviour. Signed-off-by: Peter Neuroth --- tests/plugins/htlc_accepted-customtlv.py | 38 +++++++++++++++++++++ tests/test_plugin.py | 42 ++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100755 tests/plugins/htlc_accepted-customtlv.py diff --git a/tests/plugins/htlc_accepted-customtlv.py b/tests/plugins/htlc_accepted-customtlv.py new file mode 100755 index 000000000..8358ad2bd --- /dev/null +++ b/tests/plugins/htlc_accepted-customtlv.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""A simply plugin that returns a custom tlv stream (byte encoded) to be +attached to a forwarding HTLC. +""" + +from pyln.client import Plugin + + +plugin = Plugin() +custom_tlvs = None + + +@plugin.hook("htlc_accepted") +def on_htlc_accepted(htlc, onion, plugin, **kwargs): + if 'extra_tlvs' in htlc: + print(f"called htlc accepted hook with extra_tlvs: {htlc['extra_tlvs']}") + print(f'returning continue with custom extra_tlvs: {custom_tlvs}') + if custom_tlvs: + return {"result": "continue", "extra_tlvs": custom_tlvs} + return {"result": "continue"} + + +@plugin.method("setcustomtlvs") +def setcustomtlvs(plugin, tlvs): + """Sets the custom tlv to return when receiving an incoming HTLC. + """ + global custom_tlvs + print(f'setting custom tlv to {tlvs}') + custom_tlvs = tlvs + + +@plugin.init() +def on_init(**kwargs): + global custom_tlvs + custom_tlvs = None + + +plugin.run() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index ece9a020d..d187ac61e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2419,6 +2419,48 @@ def test_htlc_accepted_hook_failmsg(node_factory): l1.rpc.pay(inv) +def test_htlc_accepted_hook_customtlvs(node_factory): + """ Passes an custom extra tlv field to the hooks return that should be set + as the `update_add_htlc_tlvs` in the `update_add_htlc` message on + forwards. + """ + plugin = os.path.join(os.path.dirname(__file__), 'plugins/htlc_accepted-customtlv.py') + l1, l2, l3 = node_factory.line_graph(3, opts=[{}, {'plugin': plugin}, {'plugin': plugin}], wait_for_announce=True) + + # Single tlv - Check that we receive the extra tlv at l3 attached by l2. + single_tlv = "fe00010001012a" # represents type: 65537, lenght: 1, value: 42 + l2.rpc.setcustomtlvs(tlvs=single_tlv) + inv = l3.rpc.invoice(1000, 'customtlvs-singletlv', '')['bolt11'] + l1.rpc.pay(inv) + l3.daemon.wait_for_log(f"called htlc accepted hook with extra_tlvs: {single_tlv}") + + # Mutliple tlvs - Check that we recieve multiple extra tlvs at l3 attached by l2. + multi_tlv = "fdffff012afe00010001020539" # represents type: 65535, length: 1, value: 42 and type: 65537, length: 2, value: 1337 + l2.rpc.setcustomtlvs(tlvs=multi_tlv) + inv = l3.rpc.invoice(1000, 'customtlvs-multitlvs', '')['bolt11'] + l1.rpc.pay(inv) + l3.daemon.wait_for_log(f"called htlc accepted hook with extra_tlvs: {multi_tlv}") + + +def test_htlc_accepted_hook_malformedtlvs(node_factory): + """ Passes an custom extra tlv field to the hooks return that is malformed + and should cause a broken log. + l1 -- l2 -- l3 + """ + plugin = os.path.join(os.path.dirname(__file__), 'plugins/htlc_accepted-customtlv.py') + l1, l2, l3 = node_factory.line_graph(3, opts=[{}, {'plugin': plugin, 'broken_log': "lightningd: ", 'may_fail': True}, {}], wait_for_announce=True) + + mal_tlv = "fe00010001020539fdffff012a" # is malformed, types are 65537 and 65535 not in asc order. + l2.rpc.setcustomtlvs(tlvs=mal_tlv) + inv = l3.rpc.invoice(1000, 'customtlvs-maltlvs', '') + phash = inv['payment_hash'] + route = l1.rpc.getroute(l3.info['id'], 1000, 1)['route'] + + # Here shouldn't use `pay` command because l2 should fail with a broken log. + l1.rpc.sendpay(route, phash, payment_secret=inv['payment_secret']) + assert l2.daemon.wait_for_log("BROKEN.*htlc_accepted_hook returned bad extra_tlvs") + + def test_hook_dep(node_factory): dep_a = os.path.join(os.path.dirname(__file__), 'plugins/dep_a.py') dep_b = os.path.join(os.path.dirname(__file__), 'plugins/dep_b.py')