Skip to content

Commit 21fb760

Browse files
nepetrustyrussell
authored andcommitted
lightningd: add invoice_amt to the htlc_accepted hook
This commit introduces a new field `invoice_msat` to the htlc_accepted hook. If this field is specified it will replace the amount of the invoice that belongs to the payment_hash of the HTLC on internal checks. This is useful in scenarios where we actually expect a smaller amount than initially specified in an invoice. Changelog-Changed: Plugins: `htlc_accepted` hook can now override the expected total amount of the invoice that belongs to the HTLC. Signed-off-by: Peter Neuroth <pet.v.ne@gmail.com>
1 parent 572c455 commit 21fb760

File tree

8 files changed

+96
-6
lines changed

8 files changed

+96
-6
lines changed

doc/developers-guide/plugin-development/hooks.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@ It can also specify `forward_to` in the response, replacing the destination. Th
462462

463463
Also, it can specify `extra_tlvs` in the response. This will replace the TLV-stream `update_add_htlc_tlvs` in the `update_add_htlc` message for forwarded htlcs.
464464

465+
If the node is the final destination, the plugin can also replace the amount of the invoice that belongs to the `payment_hash` by specifying `invoice_msat`.
466+
465467
```json
466468
{
467469
"result": "fail",

lightningd/htlc_set.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ void htlc_set_add_(struct lightningd *ld,
133133
struct logger *log,
134134
struct amount_msat msat,
135135
struct amount_msat total_msat,
136+
const struct amount_msat *invoice_msat_override,
136137
const struct sha256 *payment_hash,
137138
const struct secret *payment_secret,
138139
void (*fail)(void *, const u8 *),
@@ -151,7 +152,8 @@ void htlc_set_add_(struct lightningd *ld,
151152
* - Note: "amount paid" specified there is the `total_msat` field.
152153
*/
153154
details = invoice_check_payment(tmpctx, ld, payment_hash, total_msat,
154-
NULL, payment_secret, &err);
155+
invoice_msat_override, payment_secret,
156+
&err);
155157
if (!details) {
156158
log_debug(log, "payment failed: %s", err);
157159
fail(arg, take(failmsg_incorrect_or_unknown(NULL, ld, msat)));

lightningd/htlc_set.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,17 @@ void htlc_set_add_(struct lightningd *ld,
6060
struct logger *log,
6161
struct amount_msat msat,
6262
struct amount_msat total_msat,
63+
const struct amount_msat *invoice_msat_override,
6364
const struct sha256 *payment_hash,
6465
const struct secret *payment_secret,
6566
void (*fail)(void *, const u8 *),
6667
void (*succeeded)(void *, const struct preimage *),
6768
void *arg);
6869

69-
#define htlc_set_add(ld, log, msat, total_msat, payment_hash, payment_secret, \
70-
fail, succeeded, arg) \
71-
htlc_set_add_((ld), (log), (msat), (total_msat), (payment_hash), \
70+
#define htlc_set_add(ld, log, msat, total_msat, invoice_msat_override, \
71+
payment_hash, payment_secret, fail, succeeded, arg)\
72+
htlc_set_add_((ld), (log), (msat), (total_msat), \
73+
(invoice_msat_override), (payment_hash), \
7274
(payment_secret), \
7375
typesafe_cb_postargs(void, void *, \
7476
(fail), (arg), \

lightningd/pay.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1973,7 +1973,7 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
19731973
* not resolve immediately */
19741974
fixme_ignore(command_still_pending(cmd));
19751975
htlc_set_add(cmd->ld, cmd->ld->log, *msat, *payload->total_msat,
1976-
payment_hash, payload->payment_secret,
1976+
NULL, payment_hash, payload->payment_secret,
19771977
selfpay_mpp_fail, selfpay_mpp_succeeded,
19781978
selfpay);
19791979
return command_its_complicated("htlc_set_add may have immediately succeeded or failed");

lightningd/peer_htlcs.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
#include <ccan/mem/mem.h>
44
#include <ccan/tal/str/str.h>
55
#include <channeld/channeld_wiregen.h>
6+
#include <common/amount.h>
67
#include <common/blinding.h>
78
#include <common/ecdh.h>
89
#include <common/json_command.h>
10+
#include <common/json_parse.h>
911
#include <common/onion_decode.h>
1012
#include <common/onionreply.h>
1113
#include <common/timeout.h>
@@ -435,6 +437,7 @@ static void handle_localpay(struct htlc_in *hin,
435437
struct amount_msat amt_to_forward,
436438
u32 outgoing_cltv_value,
437439
struct amount_msat total_msat,
440+
const struct amount_msat *invoice_msat_override,
438441
const struct secret *payment_secret,
439442
const u8 *payment_metadata)
440443
{
@@ -525,6 +528,7 @@ static void handle_localpay(struct htlc_in *hin,
525528

526529
htlc_set_add(ld, hin->key.channel->log,
527530
hin->msat, total_msat,
531+
invoice_msat_override,
528532
&hin->payment_hash,
529533
payment_secret,
530534
local_fail_in_htlc,
@@ -947,6 +951,9 @@ struct htlc_accepted_hook_payload {
947951
size_t failtlvpos;
948952
const char *failexplanation;
949953
u8 *extra_tlvs_raw;
954+
/* Default is NULL, if NOT NULL: used to override the amount of the
955+
* invoice this htlc belongs to in checks! */
956+
struct amount_msat *invoice_msat;
950957
};
951958

952959
static void
@@ -1003,7 +1010,8 @@ static bool htlc_accepted_hook_deserialize(struct htlc_accepted_hook_payload *re
10031010
struct htlc_in *hin = request->hin;
10041011
struct lightningd *ld = request->ld;
10051012
struct preimage payment_preimage;
1006-
const jsmntok_t *resulttok, *paykeytok, *payloadtok, *fwdtok, *extra_tlvs_tok;
1013+
const jsmntok_t *resulttok, *paykeytok, *payloadtok, *fwdtok, *extra_tlvs_tok,
1014+
*invmsattok;
10071015
u8 *failonion, *raw_tlvs;
10081016

10091017
if (!toks || !buffer)
@@ -1018,6 +1026,17 @@ static bool htlc_accepted_hook_deserialize(struct htlc_accepted_hook_payload *re
10181026
json_strdup(tmpctx, buffer, toks));
10191027
}
10201028

1029+
invmsattok = json_get_member(buffer, toks, "invoice_msat");
1030+
if (invmsattok) {
1031+
tal_free(request->invoice_msat);
1032+
request->invoice_msat = tal(request, struct amount_msat);
1033+
if (!json_to_msat(buffer, invmsattok, request->invoice_msat)) {
1034+
fatal("Bad invoice_msat for htlc_accepted hook: %.*s",
1035+
invmsattok->end - invmsattok->start,
1036+
buffer + invmsattok->start);
1037+
}
1038+
}
1039+
10211040
extra_tlvs_tok = json_get_member(buffer, toks, "extra_tlvs");
10221041
if (extra_tlvs_tok) {
10231042
size_t max;
@@ -1275,6 +1294,7 @@ htlc_accepted_hook_final(struct htlc_accepted_hook_payload *request STEALS)
12751294
request->payload->amt_to_forward,
12761295
request->payload->outgoing_cltv,
12771296
*request->payload->total_msat,
1297+
request->invoice_msat,
12781298
request->payload->payment_secret,
12791299
request->payload->payment_metadata);
12801300

@@ -1555,6 +1575,11 @@ static bool peer_accepted_htlc(const tal_t *ctx,
15551575
hook_payload->extra_tlvs_raw = NULL;
15561576
}
15571577

1578+
/* We don't set the invoice amount here, if it is set during a hook
1579+
* response, it will be used to override the actual invoice amount on
1580+
* later checks. */
1581+
hook_payload->invoice_msat = NULL;
1582+
15581583
plugin_hook_call_htlc_accepted(ld, NULL, hook_payload);
15591584

15601585
/* Falling through here is ok, after all the HTLC locked */
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env python3
2+
"""A plugin that overrides the amount of the invoice that belongs to an HTLC."""
3+
4+
from pyln.client import Plugin
5+
6+
7+
plugin = Plugin()
8+
9+
10+
@plugin.hook("htlc_accepted")
11+
def on_htlc_accepted(htlc, onion, plugin, **kwargs):
12+
res = {"result": "continue"}
13+
if plugin.invoice_msat:
14+
res["invoice_msat"] = plugin.invoice_msat
15+
return res
16+
17+
18+
@plugin.method("setinvoicemsat")
19+
def setinvoicemsat(plugin, msat: int):
20+
"""Sets invoice_msat for the htlc_accepted response."""
21+
plugin.invoice_msat = msat
22+
23+
24+
@plugin.init()
25+
def on_init(**kwargs):
26+
plugin.invoice_msat = None
27+
28+
29+
plugin.run()

tests/test_pay.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7133,3 +7133,32 @@ def test_htlc_tlv_crash(node_factory):
71337133

71347134
l1.rpc.waitsendpay(inv1['payment_hash'], TIMEOUT)
71357135
l1.rpc.waitsendpay(inv2['payment_hash'], TIMEOUT)
7136+
7137+
7138+
def test_invoice_amount_override(node_factory):
7139+
"""Uses the htlc_accepted hook response value `invoice_msat` to override
7140+
the expected total payment amount of the invoice.
7141+
"""
7142+
plugin = os.path.join(os.path.dirname(__file__), "plugins/override_invoice_msat.py")
7143+
l1, l2 = node_factory.line_graph(2, opts=[{}, {"plugin": plugin}])
7144+
7145+
inv = l2.rpc.invoice(10_000, "expected_amt_override", "expected_amt_override")
7146+
7147+
route = [
7148+
{
7149+
"amount_msat": 1_000, # Reduced amount that is below the expected amount
7150+
"id": l2.info["id"],
7151+
"delay": 10,
7152+
"channel": first_scid(l1, l2),
7153+
}
7154+
]
7155+
7156+
with pytest.raises(RpcError):
7157+
l1.rpc.sendpay(route, inv["payment_hash"], payment_secret=inv["payment_secret"])
7158+
l1.rpc.waitsendpay(inv["payment_hash"])
7159+
7160+
# Override expected invoice amount, via htlc_accepted.
7161+
l2.rpc.setinvoicemsat(msat=1_000)
7162+
7163+
l1.rpc.sendpay(route, inv["payment_hash"], payment_secret=inv["payment_secret"])
7164+
assert l1.rpc.waitsendpay(inv["payment_hash"])["status"] == "complete"

wallet/test/run-wallet.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ void htlc_set_add_(struct lightningd *ld UNNEEDED,
380380
struct logger *log UNNEEDED,
381381
struct amount_msat msat UNNEEDED,
382382
struct amount_msat total_msat UNNEEDED,
383+
const struct amount_msat *invoice_msat_override UNNEEDED,
383384
const struct sha256 *payment_hash UNNEEDED,
384385
const struct secret *payment_secret UNNEEDED,
385386
void (*fail)(void * UNNEEDED, const u8 *) UNNEEDED,

0 commit comments

Comments
 (0)