Skip to content

Commit 5d5741e

Browse files
committed
libplugin: correctly wrap notifications we send in the notification name.
All the core notifications changed over to wrapping the notification fields in an object with the name of the notification, but notifications from plugins were missed. Changelog-Added: Plugins: `channel_hint_update`, `pay_failure` and `pay_success` notifications now have objects of the same name containing the expected fields. Changelog-Deprecated: Plugins: `channel_hint_update`, `pay_failure` and `pay_success` notification fields outside the same-named object. Signed-off-by: Rusty Russell <[email protected]>
1 parent 16819f3 commit 5d5741e

File tree

11 files changed

+229
-114
lines changed

11 files changed

+229
-114
lines changed

doc/developers-guide/deprecated-features.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ hidden: false
2323
| coin_movement.txid | Notification Field | v25.09 | v26.09 | Use `spending_txid` instead |
2424
| channel_state_changed.null_scid | Notification Field | v25.09 | v26.09 | In channel_state_changed notification, `short_channel_id` will be missing instead of `null` |
2525
| notification.payload | Notification Field | v25.09 | v26.09 | Notifications from plugins used to have fields in `payload` sub-object, now they are not (just like normal notifications) |
26+
| pay_notifications.raw_fields | Field | v25.09 | v26.09 | `channel_hint_update`, `pay_failure` and `pay_success` notifications now wrap members in an object of the same name |
2627

2728
Inevitably there are features which need to change: either to be generalized, or removed when they can no longer be supported.
2829

plugins/channel_hint.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ struct channel_hint *channel_hint_from_json(const tal_t *ctx,
191191
payload = json_get_member(buffer, toks, "payload");
192192
/* Modern API includes fields directly */
193193
if (!payload) {
194-
jhint = json_get_member(buffer, toks, "channel_hint");
194+
jhint = json_get_member(buffer, toks, "channel_hint_update");
195195
} else {
196196
jhint = json_get_member(buffer, payload, "channel_hint");
197197
}

plugins/libplugin-pay.c

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -383,15 +383,32 @@ void payment_start(struct payment *p)
383383
* We share the channel_hints across payments, and across plugins, in order
384384
* to maximize the context they have when performing payments.
385385
*/
386+
static void channel_hint_notify_core(struct plugin *plugin,
387+
struct json_stream *js,
388+
const char *fieldname,
389+
const struct channel_hint *hint)
390+
{
391+
/* The timestamp used to decay the observation over time. */
392+
channel_hint_to_json(fieldname, hint, js);
393+
}
394+
386395
static void channel_hint_notify(struct plugin *plugin,
387396
const struct channel_hint *hint)
388397
{
389398
struct json_stream *js =
390-
plugin_notification_start(plugin, "channel_hint_update");
399+
plugin_notification_start_obs(plugin, "channel_hint_update");
391400

392-
/* The timestamp used to decay the observation over time. */
393-
channel_hint_to_json("channel_hint", hint, js);
394-
plugin_notification_end(plugin, js);
401+
/* Fake up the old "payload" style, *and* the old unwrapped style */
402+
if (notification_deprecated_out_ok(plugin, "notification", "payload",
403+
"v25.09", "v26.09")) {
404+
json_add_string(js, "origin", "pay");
405+
json_object_start(js, "payload");
406+
channel_hint_notify_core(plugin, js, "channel_hint", hint);
407+
json_object_end(js);
408+
}
409+
410+
channel_hint_notify_core(plugin, js, "channel_hint_update", hint);
411+
plugin_notification_end_obs(plugin, js);
395412
}
396413

397414
static void channel_hints_update(struct payment *p,
@@ -2077,21 +2094,38 @@ static void payment_json_add_attempts(struct json_stream *s,
20772094
json_array_end(s);
20782095
}
20792096

2080-
static void payment_notify_failure(struct payment *p, const char *error_message)
2097+
static void payment_failure_notify_core(struct payment *p,
2098+
struct json_stream *n,
2099+
const char *error_message)
20812100
{
20822101
struct payment *root = payment_root(p);
2083-
struct json_stream *n;
2084-
2085-
n = plugin_notification_start(p->plugin, "pay_failure");
20862102
json_add_sha256(n, "payment_hash", p->payment_hash);
20872103
if (root->invstring != NULL)
20882104
json_add_string(n, "bolt11", root->invstring);
20892105

20902106
json_object_start(n, "error");
20912107
json_add_string(n, "message", error_message);
20922108
json_object_end(n); /* .error */
2109+
}
2110+
2111+
static void payment_notify_failure(struct payment *p, const char *error_message)
2112+
{
2113+
struct json_stream *n;
20932114

2094-
plugin_notification_end(p->plugin, n);
2115+
n = plugin_notification_start_obs(p->plugin, "pay_failure");
2116+
/* Fake up the old "payload" style, *and* the old unwrapped style */
2117+
if (notification_deprecated_out_ok(p->plugin, "notification", "payload",
2118+
"v25.09", "v26.09")) {
2119+
json_add_string(n, "origin", "pay");
2120+
json_object_start(n, "payload");
2121+
payment_failure_notify_core(p, n, error_message);
2122+
json_object_end(n);
2123+
}
2124+
2125+
json_object_start(n, "pay_failure");
2126+
payment_failure_notify_core(p, n, error_message);
2127+
json_object_end(n);
2128+
plugin_notification_end_obs(p->plugin, n);
20952129
}
20962130

20972131
/* Code shared by selfpay fast-path: populate JSON output for successful
@@ -2126,11 +2160,24 @@ void json_add_payment_success(struct json_stream *js,
21262160
json_add_preimage(js, "payment_preimage", preimage);
21272161
json_add_string(js, "status", "complete");
21282162

2129-
n = plugin_notification_start(p->plugin, "pay_success");
2163+
n = plugin_notification_start_obs(p->plugin, "pay_success");
2164+
/* Fake up the old "payload" style, *and* the old unwrapped style */
2165+
if (notification_deprecated_out_ok(p->plugin, "notification", "payload",
2166+
"v25.09", "v26.09")) {
2167+
json_add_string(n, "origin", "pay");
2168+
json_object_start(n, "payload");
2169+
json_add_sha256(n, "payment_hash", p->payment_hash);
2170+
if (root->invstring != NULL)
2171+
json_add_string(n, "bolt11", root->invstring);
2172+
json_object_end(n);
2173+
}
2174+
2175+
json_object_start(n, "pay_success");
21302176
json_add_sha256(n, "payment_hash", p->payment_hash);
21312177
if (root->invstring != NULL)
21322178
json_add_string(n, "bolt11", root->invstring);
2133-
plugin_notification_end(p->plugin, n);
2179+
json_object_end(n);
2180+
plugin_notification_end_obs(p->plugin, n);
21342181
}
21352182

21362183
/* This function is called whenever a payment ends up in a final state, or all

plugins/libplugin.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,19 @@ bool command_deprecated_out_ok(struct command *cmd,
239239
NULL, NULL);
240240
}
241241

242+
bool notification_deprecated_out_ok(struct plugin *plugin,
243+
const char *method,
244+
const char *fieldname,
245+
const char *depr_start,
246+
const char *depr_end)
247+
{
248+
return deprecated_ok(plugin->deprecated_ok,
249+
tal_fmt(tmpctx, "%s.%s", method, fieldname),
250+
depr_start, depr_end,
251+
plugin->beglist,
252+
NULL, NULL);
253+
}
254+
242255
static void ld_send(struct plugin *plugin, struct json_stream *stream)
243256
{
244257
struct jstream *jstr = tal(plugin, struct jstream);
@@ -1859,8 +1872,8 @@ void plugin_gossmap_logcb(struct plugin *plugin,
18591872
va_end(ap);
18601873
}
18611874

1862-
struct json_stream *plugin_notification_start(const tal_t *ctx,
1863-
const char *method)
1875+
struct json_stream *plugin_notification_start_obs(const tal_t *ctx,
1876+
const char *method)
18641877
{
18651878
struct json_stream *js = new_json_stream(ctx, NULL, NULL);
18661879

@@ -1872,11 +1885,26 @@ struct json_stream *plugin_notification_start(const tal_t *ctx,
18721885
return js;
18731886
}
18741887

1888+
void plugin_notification_end_obs(struct plugin *plugin,
1889+
struct json_stream *stream STEALS)
1890+
{
1891+
json_object_end(stream);
1892+
jsonrpc_finish_and_send(plugin, stream);
1893+
}
1894+
1895+
struct json_stream *plugin_notification_start(const tal_t *ctx,
1896+
const char *method)
1897+
{
1898+
struct json_stream *js = plugin_notification_start_obs(ctx, method);
1899+
json_object_start(js, method);
1900+
return js;
1901+
}
1902+
18751903
void plugin_notification_end(struct plugin *plugin,
18761904
struct json_stream *stream STEALS)
18771905
{
18781906
json_object_end(stream);
1879-
jsonrpc_finish_and_send(plugin, stream);
1907+
plugin_notification_end_obs(plugin, stream);
18801908
}
18811909

18821910
struct json_stream *plugin_notify_start(struct command *cmd, const char *method)

plugins/libplugin.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,13 @@ bool command_deprecated_in_named_ok(struct command *cmd,
381381
const char *depr_start,
382382
const char *depr_end);
383383

384+
/* Should we include this field in the notification? */
385+
bool notification_deprecated_out_ok(struct plugin *plugin,
386+
const char *method,
387+
const char *fieldname,
388+
const char *depr_start,
389+
const char *depr_end);
390+
384391
/* Call this on fatal error. */
385392
void NORETURN PRINTF_FMT(2,3) plugin_err(struct plugin *p, const char *fmt, ...);
386393

@@ -539,6 +546,12 @@ struct json_stream *plugin_notification_start(const tal_t *ctx,
539546
void plugin_notification_end(struct plugin *plugin,
540547
struct json_stream *stream STEALS);
541548

549+
/* Obsolete versions: do not use for new code! */
550+
struct json_stream *plugin_notification_start_obs(const tal_t *ctx,
551+
const char *method);
552+
void plugin_notification_end_obs(struct plugin *plugin,
553+
struct json_stream *stream TAKES);
554+
542555
/* Convenience wrapper for notify "message" */
543556
void plugin_notify_message(struct command *cmd,
544557
enum log_level level,

plugins/test/run-route-calc.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,13 @@ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memt
284284
/* Generated stub for memleak_scan_htable */
285285
void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED)
286286
{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); }
287+
/* Generated stub for notification_deprecated_out_ok */
288+
bool notification_deprecated_out_ok(struct plugin *plugin UNNEEDED,
289+
const char *method UNNEEDED,
290+
const char *fieldname UNNEEDED,
291+
const char *depr_start UNNEEDED,
292+
const char *depr_end UNNEEDED)
293+
{ fprintf(stderr, "notification_deprecated_out_ok called!\n"); abort(); }
287294
/* Generated stub for notleak_ */
288295
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
289296
{ fprintf(stderr, "notleak_ called!\n"); abort(); }
@@ -299,14 +306,14 @@ void plugin_gossmap_logcb(struct plugin *plugin UNNEEDED,
299306
/* Generated stub for plugin_log */
300307
void plugin_log(struct plugin *p UNNEEDED, enum log_level l UNNEEDED, const char *fmt UNNEEDED, ...)
301308
{ fprintf(stderr, "plugin_log called!\n"); abort(); }
302-
/* Generated stub for plugin_notification_end */
303-
void plugin_notification_end(struct plugin *plugin UNNEEDED,
304-
struct json_stream *stream STEALS UNNEEDED)
305-
{ fprintf(stderr, "plugin_notification_end called!\n"); abort(); }
306-
/* Generated stub for plugin_notification_start */
307-
struct json_stream *plugin_notification_start(const tal_t *ctx UNNEEDED,
308-
const char *method UNNEEDED)
309-
{ fprintf(stderr, "plugin_notification_start called!\n"); abort(); }
309+
/* Generated stub for plugin_notification_end_obs */
310+
void plugin_notification_end_obs(struct plugin *plugin UNNEEDED,
311+
struct json_stream *stream TAKES UNNEEDED)
312+
{ fprintf(stderr, "plugin_notification_end_obs called!\n"); abort(); }
313+
/* Generated stub for plugin_notification_start_obs */
314+
struct json_stream *plugin_notification_start_obs(const tal_t *ctx UNNEEDED,
315+
const char *method UNNEEDED)
316+
{ fprintf(stderr, "plugin_notification_start_obs called!\n"); abort(); }
310317
/* Generated stub for plugin_notify_message */
311318
void plugin_notify_message(struct command *cmd UNNEEDED,
312319
enum log_level level UNNEEDED,

plugins/test/run-route-overlong.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,13 @@ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memt
281281
/* Generated stub for memleak_scan_htable */
282282
void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED)
283283
{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); }
284+
/* Generated stub for notification_deprecated_out_ok */
285+
bool notification_deprecated_out_ok(struct plugin *plugin UNNEEDED,
286+
const char *method UNNEEDED,
287+
const char *fieldname UNNEEDED,
288+
const char *depr_start UNNEEDED,
289+
const char *depr_end UNNEEDED)
290+
{ fprintf(stderr, "notification_deprecated_out_ok called!\n"); abort(); }
284291
/* Generated stub for notleak_ */
285292
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
286293
{ fprintf(stderr, "notleak_ called!\n"); abort(); }
@@ -296,14 +303,14 @@ void plugin_gossmap_logcb(struct plugin *plugin UNNEEDED,
296303
/* Generated stub for plugin_log */
297304
void plugin_log(struct plugin *p UNNEEDED, enum log_level l UNNEEDED, const char *fmt UNNEEDED, ...)
298305
{ fprintf(stderr, "plugin_log called!\n"); abort(); }
299-
/* Generated stub for plugin_notification_end */
300-
void plugin_notification_end(struct plugin *plugin UNNEEDED,
301-
struct json_stream *stream STEALS UNNEEDED)
302-
{ fprintf(stderr, "plugin_notification_end called!\n"); abort(); }
303-
/* Generated stub for plugin_notification_start */
304-
struct json_stream *plugin_notification_start(const tal_t *ctx UNNEEDED,
305-
const char *method UNNEEDED)
306-
{ fprintf(stderr, "plugin_notification_start called!\n"); abort(); }
306+
/* Generated stub for plugin_notification_end_obs */
307+
void plugin_notification_end_obs(struct plugin *plugin UNNEEDED,
308+
struct json_stream *stream TAKES UNNEEDED)
309+
{ fprintf(stderr, "plugin_notification_end_obs called!\n"); abort(); }
310+
/* Generated stub for plugin_notification_start_obs */
311+
struct json_stream *plugin_notification_start_obs(const tal_t *ctx UNNEEDED,
312+
const char *method UNNEEDED)
313+
{ fprintf(stderr, "plugin_notification_start_obs called!\n"); abort(); }
307314
/* Generated stub for plugin_notify_message */
308315
void plugin_notify_message(struct command *cmd UNNEEDED,
309316
enum log_level level UNNEEDED,

tests/plugins/custom_notifications.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ def faulty_emit(plugin):
2525

2626

2727
@plugin.subscribe("pay_success")
28-
def on_pay_success(origin, payment_hash, **kwargs):
28+
def on_pay_success(origin, pay_success, **kwargs):
2929
plugin.log(
3030
"Got a pay_success notification from plugin {} for payment_hash {}".format(
3131
origin,
32-
payment_hash
32+
pay_success['payment_hash']
3333
)
3434
)
3535

tests/test_plugin.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4374,10 +4374,12 @@ def test_peer_storage(node_factory, bitcoind):
43744374
assert not l2.daemon.is_in_log(r'PeerStorageFailed')
43754375

43764376

4377-
@pytest.mark.xfail(strict=True)
4378-
def test_pay_plugin_notifications(node_factory, bitcoind, chainparams):
4377+
@pytest.mark.parametrize("deprecated", [False, True])
4378+
def test_pay_plugin_notifications(node_factory, bitcoind, chainparams, deprecated):
43794379
plugin = os.path.join(os.getcwd(), 'tests/plugins/all_notifications.py')
43804380
opts = {"plugin": plugin}
4381+
if deprecated:
4382+
opts['allow-deprecated-apis'] = True
43814383

43824384
l1, l2, l3 = node_factory.line_graph(3, opts=[opts, {}, {}],
43834385
wait_for_announce=True)
@@ -4403,15 +4405,17 @@ def zero_timestamps(obj):
44034405
dict_str = line.split("notification channel_hint_update: ", 1)[1]
44044406
data = zero_timestamps(ast.literal_eval(dict_str))
44054407

4406-
# pyln-client's plugin.py duplicated payload into same name as update.
44074408
channel_hint_update_core = {'scid': first_scid(l1, l2) + '/1',
44084409
'estimated_capacity_msat': 964719000 if chainparams['elements'] else 978718000,
44094410
'total_capacity_msat': 1000000000,
44104411
'timestamp': 0,
44114412
'enabled': True}
44124413
channel_hint_update = {'origin': 'pay',
4413-
'payload': {'channel_hint': channel_hint_update_core},
4414-
'channel_hint_update': {'channel_hint': channel_hint_update_core}}
4414+
'channel_hint_update': channel_hint_update_core}
4415+
if deprecated:
4416+
# pyln-client's plugin.py duplicated payload into same name as update.
4417+
channel_hint_update['payload'] = {'channel_hint': channel_hint_update_core}
4418+
44154419
assert data == channel_hint_update
44164420

44174421
# It gets a success notification
@@ -4420,10 +4424,11 @@ def zero_timestamps(obj):
44204424
data = ast.literal_eval(dict_str)
44214425
success_core = {'payment_hash': inv1['payment_hash'],
44224426
'bolt11': inv1['bolt11']}
4423-
# Includes deprecated and modern. pyln-client plugin.py copies fields as necessary.
44244427
success = {'origin': 'pay',
4425-
'payload': success_core,
44264428
'pay_success': success_core}
4429+
if deprecated:
4430+
# pyln-client's plugin.py duplicated payload into same name as update.
4431+
success['payload'] = success_core
44274432
assert data == success
44284433

44294434
inv2 = l3.rpc.invoice(10000, "second", "desc")
@@ -4435,10 +4440,11 @@ def zero_timestamps(obj):
44354440
dict_str = line.split("notification pay_failure: ", 1)[1]
44364441
data = ast.literal_eval(dict_str)
44374442
failure_core = {'payment_hash': inv2['payment_hash'], 'bolt11': inv2['bolt11'], 'error': {'message': 'failed: WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS (reply from remote)'}}
4438-
# Includes deprecated and modern.
44394443
failure = {'origin': 'pay',
4440-
'payload': failure_core,
44414444
'pay_failure': failure_core}
4445+
if deprecated:
4446+
# pyln-client's plugin.py duplicated payload into same name as update.
4447+
failure['payload'] = failure_core
44424448
assert data == failure
44434449

44444450

0 commit comments

Comments
 (0)