From 8ce0fda05b5a2ff492444c6b4dd547116dd6085f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Aug 2025 10:42:11 +0930 Subject: [PATCH] offers: loosen payment_constraints on invoices' blinded paths. In practice, we were too strict. Here's Phoenix paying my node via another node: ``` 2025-08-25T13:57:53.311Z DEBUG 02...-chan#216: Failing HTLC because of an invalid payload (TLV 10 pos 103): cltv_expiry 911816 > payment_constraint 911721 ``` We add 6 blocks, but this is supposed to be the *max* allowed. Increase it to 1008, to allow shadow padding. Here are the CLTV delays across advertized channels in the network: most are far less than this: Count Delay 1 0 1 4 3 12 899 18 602 20 1 22 3 24 1 25 9 26 5 29 72 30 54 32 4352 34 2 35 6 36 10 37 27 38 8 39 15256 40 94 42 102 44 13 45 20 46 87 48 230 50 100 51 14 55 316 60 29 64 30 65 24 68 82 69 9 70 779 72 13 77 243 78 1 79 26793 80 1 82 6 83 1 84 18 85 1 86 9 87 16 88 101 89 2 90 11 96 113 99 9527 100 41 112 34 118 132 119 403 120 24 128 232 140 138 142 14303 144 2 145 41 150 6 160 3 172 8 174 27 180 4 190 297 200 99 210 34 216 219 222 15 240 105 288 17 300 7 336 1 360 19 400 24 420 26 432 20 450 1 480 12 500 1 720 1 850 1 1000 1 1002 1 1144 1 1192 5 1201 1 1444 1 1795 1 1900 1 2016 Signed-off-by: Rusty Russell Changelog-Changed: Protocol: Offers on nodes with only private channels are now payable (i.e. no more blinded path errors!). Fixes: https://github.com/ElementsProject/lightning/issues/7718 --- plugins/offers_invreq_hook.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 5cefec997537..c8fe8d6e5ed4 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -292,15 +292,28 @@ static struct command_result *found_best_peer(struct command *cmd, * after `invoice_created_at`: * - MUST set `invoice_relative_expiry` */ - /* Give them 6 blocks, plus one per 10 minutes until expiry. */ if (ir->inv->invoice_relative_expiry) - base = blockheight + 6 + *ir->inv->invoice_relative_expiry / 600; + base = blockheight + *ir->inv->invoice_relative_expiry / 600; else - base = blockheight + 6 + 7200 / 600; - + base = blockheight + 7200 / 600; + + /* BOLT #4: + * - MUST set `encrypted_data_tlv.payment_constraints` + * for each non-final node and MAY set it for the + * final node: + * - `max_cltv_expiry` to the largest block height at which + * the route is allowed to be used, starting from the final + * node's chosen `max_cltv_expiry` height at which the route + * should expire, adding the final node's + * `min_final_cltv_expiry_delta` and then adding + * `encrypted_data_tlv.payment_relay.cltv_expiry_delta` at + * each hop. + */ + /* BUT: we also recommend padding CLTV when paying, to obscure paths: if this is too tight + * payments fail in practice! We add 1008 (half the max possible) */ etlvs[0]->payment_constraints = tal(etlvs[0], struct tlv_encrypted_data_tlv_payment_constraints); - etlvs[0]->payment_constraints->max_cltv_expiry = base + best->cltv + cltv_final; + etlvs[0]->payment_constraints->max_cltv_expiry = 1008 + base + best->cltv + cltv_final; etlvs[0]->payment_constraints->htlc_minimum_msat = best->htlc_min.millisatoshis; /* Raw: tlv */ /* So we recognize this payment */