diff --git a/lightningd/pay.c b/lightningd/pay.c index 80e9ee068f0c..010d1401bd53 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1218,7 +1218,9 @@ static struct command_result *param_route_hops(struct command *cmd, struct node_id *id; struct short_channel_id *channel; unsigned *delay, *direction; - enum route_hop_style *style; + struct pubkey *blinding; + u8 *enctlv; + enum route_hop_style *style, default_style; if (!param(cmd, buffer, t, /* Only *one* of these is required */ @@ -1229,8 +1231,9 @@ static struct command_result *param_route_hops(struct command *cmd, p_opt("delay", param_number, &delay), p_opt("channel", param_short_channel_id, &channel), p_opt("direction", param_number, &direction), - p_opt_def("style", param_route_hop_style, &style, - ROUTE_HOP_LEGACY), + p_opt("style", param_route_hop_style, &style), + p_opt("blinding", param_pubkey, &blinding), + p_opt("enctlv", param_bin_from_hex, &enctlv), NULL)) return command_param_failed(); @@ -1255,11 +1258,21 @@ static struct command_result *param_route_hops(struct command *cmd, if (!msat) msat = amount_msat; + if (blinding || enctlv) { + if (style && *style == ROUTE_HOP_LEGACY) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "%s[%zi]: Can't have blinding or enctlv with legacy", name, i); + default_style = ROUTE_HOP_TLV; + } else + default_style = ROUTE_HOP_LEGACY; + (*hops)[i].amount = *msat; (*hops)[i].nodeid = *id; (*hops)[i].delay = *delay; (*hops)[i].channel_id = *channel; - (*hops)[i].style = *style; + (*hops)[i].blinding = blinding; + (*hops)[i].enctlv = enctlv; + (*hops)[i].style = style ? *style : default_style; /* FIXME: Actually ignored by sending code! */ (*hops)[i].direction = direction ? *direction : 0; } diff --git a/tests/test_pay.py b/tests/test_pay.py index b490546cb684..d48070e19d22 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -6,7 +6,7 @@ from pyln.proto.onion import TlvPayload from utils import ( DEVELOPER, wait_for, only_one, sync_blockheight, SLOW_MACHINE, TIMEOUT, - VALGRIND + VALGRIND, EXPERIMENTAL_FEATURES ) import copy import os @@ -15,6 +15,7 @@ import re import string import struct +import subprocess import time import unittest @@ -2895,3 +2896,49 @@ def test_reject_invalid_payload(node_factory): with pytest.raises(RpcError, match=r'WIRE_INVALID_ONION_PAYLOAD'): l1.rpc.waitsendpay(inv['payment_hash']) + + +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "Needs blinding args to sendpay") +def test_sendpay_blinding(node_factory): + l1, l2, l3, l4 = node_factory.line_graph(4) + + blindedpathtool = os.path.join(os.path.dirname(__file__), "..", "devtools", "blindedpath") + + # Create blinded path l2->l4 + output = subprocess.check_output( + [blindedpathtool, '--simple-output', 'create', + l2.info['id'] + "/" + l2.get_channel_scid(l3), + l3.info['id'] + "/" + l3.get_channel_scid(l4), + l4.info['id']] + ).decode('ASCII').strip() + + # First line is blinding, then then . + blinding, p1, p1enc, p2, p2enc, p3 = output.split('\n') + # First hop can't be blinded! + assert p1 == l2.info['id'] + + amt = 10**3 + inv = l4.rpc.invoice(amt, "lbl", "desc") + + route = [{'id': l2.info['id'], + 'channel': l1.get_channel_scid(l2), + 'amount_msat': Millisatoshi(1002), + 'delay': 21, + 'blinding': blinding, + 'enctlv': p1enc}, + {'id': p2, + 'amount_msat': Millisatoshi(1001), + 'delay': 15, + # FIXME: this is a dummy! + 'channel': '0x0x0', + 'enctlv': p2enc}, + {'id': p3, + # FIXME: this is a dummy! + 'channel': '0x0x0', + 'amount_msat': Millisatoshi(1000), + 'delay': 9, + 'style': 'tlv'}] + l1.rpc.sendpay(route=route, + payment_hash=inv['payment_hash'], + bolt11=inv['bolt11']) + l1.rpc.waitsendpay(inv['payment_hash'])