From 5d5741e681b457731516682d4f5682198cf254e5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 17 Aug 2025 09:39:31 +0930 Subject: [PATCH] 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 --- doc/developers-guide/deprecated-features.md | 1 + plugins/channel_hint.c | 2 +- plugins/libplugin-pay.c | 69 +++++++-- plugins/libplugin.c | 34 ++++- plugins/libplugin.h | 13 ++ plugins/test/run-route-calc.c | 23 +-- plugins/test/run-route-overlong.c | 23 +-- tests/plugins/custom_notifications.py | 4 +- tests/test_plugin.py | 24 ++-- tests/test_xpay.py | 148 ++++++++++---------- wallet/test/run-db.c | 2 +- 11 files changed, 229 insertions(+), 114 deletions(-) diff --git a/doc/developers-guide/deprecated-features.md b/doc/developers-guide/deprecated-features.md index 3193abd48..6769e8401 100644 --- a/doc/developers-guide/deprecated-features.md +++ b/doc/developers-guide/deprecated-features.md @@ -23,6 +23,7 @@ hidden: false | coin_movement.txid | Notification Field | v25.09 | v26.09 | Use `spending_txid` instead | | 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` | | 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) | +| 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 | Inevitably there are features which need to change: either to be generalized, or removed when they can no longer be supported. diff --git a/plugins/channel_hint.c b/plugins/channel_hint.c index af5457591..1db37791e 100644 --- a/plugins/channel_hint.c +++ b/plugins/channel_hint.c @@ -191,7 +191,7 @@ struct channel_hint *channel_hint_from_json(const tal_t *ctx, payload = json_get_member(buffer, toks, "payload"); /* Modern API includes fields directly */ if (!payload) { - jhint = json_get_member(buffer, toks, "channel_hint"); + jhint = json_get_member(buffer, toks, "channel_hint_update"); } else { jhint = json_get_member(buffer, payload, "channel_hint"); } diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 0fe5472a5..ec2bcb7a2 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -383,15 +383,32 @@ void payment_start(struct payment *p) * We share the channel_hints across payments, and across plugins, in order * to maximize the context they have when performing payments. */ +static void channel_hint_notify_core(struct plugin *plugin, + struct json_stream *js, + const char *fieldname, + const struct channel_hint *hint) +{ + /* The timestamp used to decay the observation over time. */ + channel_hint_to_json(fieldname, hint, js); +} + static void channel_hint_notify(struct plugin *plugin, const struct channel_hint *hint) { struct json_stream *js = - plugin_notification_start(plugin, "channel_hint_update"); + plugin_notification_start_obs(plugin, "channel_hint_update"); - /* The timestamp used to decay the observation over time. */ - channel_hint_to_json("channel_hint", hint, js); - plugin_notification_end(plugin, js); + /* Fake up the old "payload" style, *and* the old unwrapped style */ + if (notification_deprecated_out_ok(plugin, "notification", "payload", + "v25.09", "v26.09")) { + json_add_string(js, "origin", "pay"); + json_object_start(js, "payload"); + channel_hint_notify_core(plugin, js, "channel_hint", hint); + json_object_end(js); + } + + channel_hint_notify_core(plugin, js, "channel_hint_update", hint); + plugin_notification_end_obs(plugin, js); } static void channel_hints_update(struct payment *p, @@ -2077,12 +2094,11 @@ static void payment_json_add_attempts(struct json_stream *s, json_array_end(s); } -static void payment_notify_failure(struct payment *p, const char *error_message) +static void payment_failure_notify_core(struct payment *p, + struct json_stream *n, + const char *error_message) { struct payment *root = payment_root(p); - struct json_stream *n; - - n = plugin_notification_start(p->plugin, "pay_failure"); json_add_sha256(n, "payment_hash", p->payment_hash); if (root->invstring != NULL) json_add_string(n, "bolt11", root->invstring); @@ -2090,8 +2106,26 @@ static void payment_notify_failure(struct payment *p, const char *error_message) json_object_start(n, "error"); json_add_string(n, "message", error_message); json_object_end(n); /* .error */ +} - plugin_notification_end(p->plugin, n); +static void payment_notify_failure(struct payment *p, const char *error_message) +{ + struct json_stream *n; + + n = plugin_notification_start_obs(p->plugin, "pay_failure"); + /* Fake up the old "payload" style, *and* the old unwrapped style */ + if (notification_deprecated_out_ok(p->plugin, "notification", "payload", + "v25.09", "v26.09")) { + json_add_string(n, "origin", "pay"); + json_object_start(n, "payload"); + payment_failure_notify_core(p, n, error_message); + json_object_end(n); + } + + json_object_start(n, "pay_failure"); + payment_failure_notify_core(p, n, error_message); + json_object_end(n); + plugin_notification_end_obs(p->plugin, n); } /* Code shared by selfpay fast-path: populate JSON output for successful @@ -2126,11 +2160,24 @@ void json_add_payment_success(struct json_stream *js, json_add_preimage(js, "payment_preimage", preimage); json_add_string(js, "status", "complete"); - n = plugin_notification_start(p->plugin, "pay_success"); + n = plugin_notification_start_obs(p->plugin, "pay_success"); + /* Fake up the old "payload" style, *and* the old unwrapped style */ + if (notification_deprecated_out_ok(p->plugin, "notification", "payload", + "v25.09", "v26.09")) { + json_add_string(n, "origin", "pay"); + json_object_start(n, "payload"); + json_add_sha256(n, "payment_hash", p->payment_hash); + if (root->invstring != NULL) + json_add_string(n, "bolt11", root->invstring); + json_object_end(n); + } + + json_object_start(n, "pay_success"); json_add_sha256(n, "payment_hash", p->payment_hash); if (root->invstring != NULL) json_add_string(n, "bolt11", root->invstring); - plugin_notification_end(p->plugin, n); + json_object_end(n); + plugin_notification_end_obs(p->plugin, n); } /* This function is called whenever a payment ends up in a final state, or all diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 614b5d3db..dbe58d2ca 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -239,6 +239,19 @@ bool command_deprecated_out_ok(struct command *cmd, NULL, NULL); } +bool notification_deprecated_out_ok(struct plugin *plugin, + const char *method, + const char *fieldname, + const char *depr_start, + const char *depr_end) +{ + return deprecated_ok(plugin->deprecated_ok, + tal_fmt(tmpctx, "%s.%s", method, fieldname), + depr_start, depr_end, + plugin->beglist, + NULL, NULL); +} + static void ld_send(struct plugin *plugin, struct json_stream *stream) { struct jstream *jstr = tal(plugin, struct jstream); @@ -1859,8 +1872,8 @@ void plugin_gossmap_logcb(struct plugin *plugin, va_end(ap); } -struct json_stream *plugin_notification_start(const tal_t *ctx, - const char *method) +struct json_stream *plugin_notification_start_obs(const tal_t *ctx, + const char *method) { struct json_stream *js = new_json_stream(ctx, NULL, NULL); @@ -1872,11 +1885,26 @@ struct json_stream *plugin_notification_start(const tal_t *ctx, return js; } +void plugin_notification_end_obs(struct plugin *plugin, + struct json_stream *stream STEALS) +{ + json_object_end(stream); + jsonrpc_finish_and_send(plugin, stream); +} + +struct json_stream *plugin_notification_start(const tal_t *ctx, + const char *method) +{ + struct json_stream *js = plugin_notification_start_obs(ctx, method); + json_object_start(js, method); + return js; +} + void plugin_notification_end(struct plugin *plugin, struct json_stream *stream STEALS) { json_object_end(stream); - jsonrpc_finish_and_send(plugin, stream); + plugin_notification_end_obs(plugin, stream); } struct json_stream *plugin_notify_start(struct command *cmd, const char *method) diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 2cc781da8..80261ab2a 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -381,6 +381,13 @@ bool command_deprecated_in_named_ok(struct command *cmd, const char *depr_start, const char *depr_end); +/* Should we include this field in the notification? */ +bool notification_deprecated_out_ok(struct plugin *plugin, + const char *method, + const char *fieldname, + const char *depr_start, + const char *depr_end); + /* Call this on fatal error. */ void NORETURN PRINTF_FMT(2,3) plugin_err(struct plugin *p, const char *fmt, ...); @@ -539,6 +546,12 @@ struct json_stream *plugin_notification_start(const tal_t *ctx, void plugin_notification_end(struct plugin *plugin, struct json_stream *stream STEALS); +/* Obsolete versions: do not use for new code! */ +struct json_stream *plugin_notification_start_obs(const tal_t *ctx, + const char *method); +void plugin_notification_end_obs(struct plugin *plugin, + struct json_stream *stream TAKES); + /* Convenience wrapper for notify "message" */ void plugin_notify_message(struct command *cmd, enum log_level level, diff --git a/plugins/test/run-route-calc.c b/plugins/test/run-route-calc.c index 73e56ce31..caca1ff67 100644 --- a/plugins/test/run-route-calc.c +++ b/plugins/test/run-route-calc.c @@ -284,6 +284,13 @@ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memt /* Generated stub for memleak_scan_htable */ void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) { fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } +/* Generated stub for notification_deprecated_out_ok */ +bool notification_deprecated_out_ok(struct plugin *plugin UNNEEDED, + const char *method UNNEEDED, + const char *fieldname UNNEEDED, + const char *depr_start UNNEEDED, + const char *depr_end UNNEEDED) +{ fprintf(stderr, "notification_deprecated_out_ok called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } @@ -299,14 +306,14 @@ void plugin_gossmap_logcb(struct plugin *plugin UNNEEDED, /* Generated stub for plugin_log */ void plugin_log(struct plugin *p UNNEEDED, enum log_level l UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "plugin_log called!\n"); abort(); } -/* Generated stub for plugin_notification_end */ -void plugin_notification_end(struct plugin *plugin UNNEEDED, - struct json_stream *stream STEALS UNNEEDED) -{ fprintf(stderr, "plugin_notification_end called!\n"); abort(); } -/* Generated stub for plugin_notification_start */ -struct json_stream *plugin_notification_start(const tal_t *ctx UNNEEDED, - const char *method UNNEEDED) -{ fprintf(stderr, "plugin_notification_start called!\n"); abort(); } +/* Generated stub for plugin_notification_end_obs */ +void plugin_notification_end_obs(struct plugin *plugin UNNEEDED, + struct json_stream *stream TAKES UNNEEDED) +{ fprintf(stderr, "plugin_notification_end_obs called!\n"); abort(); } +/* Generated stub for plugin_notification_start_obs */ +struct json_stream *plugin_notification_start_obs(const tal_t *ctx UNNEEDED, + const char *method UNNEEDED) +{ fprintf(stderr, "plugin_notification_start_obs called!\n"); abort(); } /* Generated stub for plugin_notify_message */ void plugin_notify_message(struct command *cmd UNNEEDED, enum log_level level UNNEEDED, diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index ad0a63ec6..e5d5e69c4 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -281,6 +281,13 @@ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memt /* Generated stub for memleak_scan_htable */ void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) { fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } +/* Generated stub for notification_deprecated_out_ok */ +bool notification_deprecated_out_ok(struct plugin *plugin UNNEEDED, + const char *method UNNEEDED, + const char *fieldname UNNEEDED, + const char *depr_start UNNEEDED, + const char *depr_end UNNEEDED) +{ fprintf(stderr, "notification_deprecated_out_ok called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } @@ -296,14 +303,14 @@ void plugin_gossmap_logcb(struct plugin *plugin UNNEEDED, /* Generated stub for plugin_log */ void plugin_log(struct plugin *p UNNEEDED, enum log_level l UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "plugin_log called!\n"); abort(); } -/* Generated stub for plugin_notification_end */ -void plugin_notification_end(struct plugin *plugin UNNEEDED, - struct json_stream *stream STEALS UNNEEDED) -{ fprintf(stderr, "plugin_notification_end called!\n"); abort(); } -/* Generated stub for plugin_notification_start */ -struct json_stream *plugin_notification_start(const tal_t *ctx UNNEEDED, - const char *method UNNEEDED) -{ fprintf(stderr, "plugin_notification_start called!\n"); abort(); } +/* Generated stub for plugin_notification_end_obs */ +void plugin_notification_end_obs(struct plugin *plugin UNNEEDED, + struct json_stream *stream TAKES UNNEEDED) +{ fprintf(stderr, "plugin_notification_end_obs called!\n"); abort(); } +/* Generated stub for plugin_notification_start_obs */ +struct json_stream *plugin_notification_start_obs(const tal_t *ctx UNNEEDED, + const char *method UNNEEDED) +{ fprintf(stderr, "plugin_notification_start_obs called!\n"); abort(); } /* Generated stub for plugin_notify_message */ void plugin_notify_message(struct command *cmd UNNEEDED, enum log_level level UNNEEDED, diff --git a/tests/plugins/custom_notifications.py b/tests/plugins/custom_notifications.py index c57a386cd..1a3d92f18 100755 --- a/tests/plugins/custom_notifications.py +++ b/tests/plugins/custom_notifications.py @@ -25,11 +25,11 @@ def faulty_emit(plugin): @plugin.subscribe("pay_success") -def on_pay_success(origin, payment_hash, **kwargs): +def on_pay_success(origin, pay_success, **kwargs): plugin.log( "Got a pay_success notification from plugin {} for payment_hash {}".format( origin, - payment_hash + pay_success['payment_hash'] ) ) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 706bda882..2ac9da76b 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -4374,10 +4374,12 @@ def test_peer_storage(node_factory, bitcoind): assert not l2.daemon.is_in_log(r'PeerStorageFailed') -@pytest.mark.xfail(strict=True) -def test_pay_plugin_notifications(node_factory, bitcoind, chainparams): +@pytest.mark.parametrize("deprecated", [False, True]) +def test_pay_plugin_notifications(node_factory, bitcoind, chainparams, deprecated): plugin = os.path.join(os.getcwd(), 'tests/plugins/all_notifications.py') opts = {"plugin": plugin} + if deprecated: + opts['allow-deprecated-apis'] = True l1, l2, l3 = node_factory.line_graph(3, opts=[opts, {}, {}], wait_for_announce=True) @@ -4403,15 +4405,17 @@ def test_pay_plugin_notifications(node_factory, bitcoind, chainparams): dict_str = line.split("notification channel_hint_update: ", 1)[1] data = zero_timestamps(ast.literal_eval(dict_str)) - # pyln-client's plugin.py duplicated payload into same name as update. channel_hint_update_core = {'scid': first_scid(l1, l2) + '/1', 'estimated_capacity_msat': 964719000 if chainparams['elements'] else 978718000, 'total_capacity_msat': 1000000000, 'timestamp': 0, 'enabled': True} channel_hint_update = {'origin': 'pay', - 'payload': {'channel_hint': channel_hint_update_core}, - 'channel_hint_update': {'channel_hint': channel_hint_update_core}} + 'channel_hint_update': channel_hint_update_core} + if deprecated: + # pyln-client's plugin.py duplicated payload into same name as update. + channel_hint_update['payload'] = {'channel_hint': channel_hint_update_core} + assert data == channel_hint_update # It gets a success notification @@ -4420,10 +4424,11 @@ def test_pay_plugin_notifications(node_factory, bitcoind, chainparams): data = ast.literal_eval(dict_str) success_core = {'payment_hash': inv1['payment_hash'], 'bolt11': inv1['bolt11']} - # Includes deprecated and modern. pyln-client plugin.py copies fields as necessary. success = {'origin': 'pay', - 'payload': success_core, 'pay_success': success_core} + if deprecated: + # pyln-client's plugin.py duplicated payload into same name as update. + success['payload'] = success_core assert data == success inv2 = l3.rpc.invoice(10000, "second", "desc") @@ -4435,10 +4440,11 @@ def test_pay_plugin_notifications(node_factory, bitcoind, chainparams): dict_str = line.split("notification pay_failure: ", 1)[1] data = ast.literal_eval(dict_str) failure_core = {'payment_hash': inv2['payment_hash'], 'bolt11': inv2['bolt11'], 'error': {'message': 'failed: WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS (reply from remote)'}} - # Includes deprecated and modern. failure = {'origin': 'pay', - 'payload': failure_core, 'pay_failure': failure_core} + if deprecated: + # pyln-client's plugin.py duplicated payload into same name as update. + failure['payload'] = failure_core assert data == failure diff --git a/tests/test_xpay.py b/tests/test_xpay.py index f9c95a307..8539fdd20 100644 --- a/tests/test_xpay.py +++ b/tests/test_xpay.py @@ -863,31 +863,33 @@ def test_attempt_notifications(node_factory): line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_start: ") dict_str = line.split("Got pay_part_start: ", 1)[1] data = zero_fields(ast.literal_eval(dict_str), ['groupid']) - expected = {'payment_hash': inv1['payment_hash'], - 'groupid': 0, - 'partid': 1, - 'total_payment_msat': 5000000, - 'attempt_msat': 5000000, - 'hops': [{'next_node': l2.info['id'], - 'short_channel_id': scid12, - 'direction': scid12_dir, - 'channel_in_msat': 5000051, - 'channel_out_msat': 5000051}, - {'next_node': l3.info['id'], - 'short_channel_id': scid23, - 'direction': scid23_dir, - 'channel_in_msat': 5000051, - 'channel_out_msat': 5000000}]} + expected = {'pay_part_start': + {'payment_hash': inv1['payment_hash'], + 'groupid': 0, + 'partid': 1, + 'total_payment_msat': 5000000, + 'attempt_msat': 5000000, + 'hops': [{'next_node': l2.info['id'], + 'short_channel_id': scid12, + 'direction': scid12_dir, + 'channel_in_msat': 5000051, + 'channel_out_msat': 5000051}, + {'next_node': l3.info['id'], + 'short_channel_id': scid23, + 'direction': scid23_dir, + 'channel_in_msat': 5000051, + 'channel_out_msat': 5000000}]}} assert data == expected line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_end: ") dict_str = line.split("Got pay_part_end: ", 1)[1] data = zero_fields(ast.literal_eval(dict_str), ('duration', 'groupid')) - expected = {'payment_hash': inv1['payment_hash'], - 'status': 'success', - 'duration': 0, - 'groupid': 0, - 'partid': 1} + expected = {'pay_part_end': + {'payment_hash': inv1['payment_hash'], + 'status': 'success', + 'duration': 0, + 'groupid': 0, + 'partid': 1}} assert data == expected inv2 = l3.rpc.invoice(10000000, 'test_attempt_notifications2', 'test_attempt_notifications2') @@ -900,35 +902,37 @@ def test_attempt_notifications(node_factory): line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_start: ") dict_str = line.split("Got pay_part_start: ", 1)[1] data = zero_fields(ast.literal_eval(dict_str), ['groupid']) - expected = {'payment_hash': inv2['payment_hash'], - 'groupid': 0, - 'partid': 1, - 'total_payment_msat': 10000000, - 'attempt_msat': 10000000, - 'hops': [{'next_node': l2.info['id'], - 'short_channel_id': scid12, - 'direction': scid12_dir, - 'channel_in_msat': 10000101, - 'channel_out_msat': 10000101}, - {'next_node': l3.info['id'], - 'short_channel_id': scid23, - 'direction': scid23_dir, - 'channel_in_msat': 10000101, - 'channel_out_msat': 10000000}]} + expected = {'pay_part_start': + {'payment_hash': inv2['payment_hash'], + 'groupid': 0, + 'partid': 1, + 'total_payment_msat': 10000000, + 'attempt_msat': 10000000, + 'hops': [{'next_node': l2.info['id'], + 'short_channel_id': scid12, + 'direction': scid12_dir, + 'channel_in_msat': 10000101, + 'channel_out_msat': 10000101}, + {'next_node': l3.info['id'], + 'short_channel_id': scid23, + 'direction': scid23_dir, + 'channel_in_msat': 10000101, + 'channel_out_msat': 10000000}]}} assert data == expected line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_end: ") dict_str = line.split("Got pay_part_end: ", 1)[1] data = zero_fields(ast.literal_eval(dict_str), ('duration', 'groupid')) - expected = {'payment_hash': inv2['payment_hash'], - 'status': 'failure', - 'duration': 0, - 'groupid': 0, - 'partid': 1, - 'failed_msg': '400f00000000009896800000006c', - 'failed_node_id': l3.info['id'], - 'error_code': 16399, - 'error_message': 'incorrect_or_unknown_payment_details'} + expected = {'pay_part_end': + {'payment_hash': inv2['payment_hash'], + 'status': 'failure', + 'duration': 0, + 'groupid': 0, + 'partid': 1, + 'failed_msg': '400f00000000009896800000006c', + 'failed_node_id': l3.info['id'], + 'error_code': 16399, + 'error_message': 'incorrect_or_unknown_payment_details'}} assert data == expected # Intermediary node failure @@ -939,36 +943,38 @@ def test_attempt_notifications(node_factory): line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_start: ") dict_str = line.split("Got pay_part_start: ", 1)[1] data = zero_fields(ast.literal_eval(dict_str), ['groupid']) - expected = {'payment_hash': inv2['payment_hash'], - 'groupid': 0, - 'partid': 1, - 'total_payment_msat': 10000000, - 'attempt_msat': 10000000, - 'hops': [{'next_node': l2.info['id'], - 'short_channel_id': scid12, - 'direction': scid12_dir, - 'channel_in_msat': 10000101, - 'channel_out_msat': 10000101}, - {'next_node': l3.info['id'], - 'short_channel_id': scid23, - 'direction': scid23_dir, - 'channel_in_msat': 10000101, - 'channel_out_msat': 10000000}]} + expected = {'pay_part_start': + {'payment_hash': inv2['payment_hash'], + 'groupid': 0, + 'partid': 1, + 'total_payment_msat': 10000000, + 'attempt_msat': 10000000, + 'hops': [{'next_node': l2.info['id'], + 'short_channel_id': scid12, + 'direction': scid12_dir, + 'channel_in_msat': 10000101, + 'channel_out_msat': 10000101}, + {'next_node': l3.info['id'], + 'short_channel_id': scid23, + 'direction': scid23_dir, + 'channel_in_msat': 10000101, + 'channel_out_msat': 10000000}]}} assert data == expected line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_end: ") dict_str = line.split("Got pay_part_end: ", 1)[1] data = zero_fields(ast.literal_eval(dict_str), ('duration', 'groupid', 'failed_msg')) - expected = {'payment_hash': inv2['payment_hash'], - 'status': 'failure', - 'duration': 0, - 'groupid': 0, - 'partid': 1, - # This includes the channel update: just zero it out - 'failed_msg': 0, - 'failed_direction': 0, - 'failed_node_id': l2.info['id'], - 'failed_short_channel_id': scid23, - 'error_code': 4103, - 'error_message': 'temporary_channel_failure'} + expected = {'pay_part_end': + {'payment_hash': inv2['payment_hash'], + 'status': 'failure', + 'duration': 0, + 'groupid': 0, + 'partid': 1, + # This includes the channel update: just zero it out + 'failed_msg': 0, + 'failed_direction': 0, + 'failed_node_id': l2.info['id'], + 'failed_short_channel_id': scid23, + 'error_code': 4103, + 'error_message': 'temporary_channel_failure'}} assert data == expected diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index a75d099ee..b5eca59ae 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -190,7 +190,7 @@ struct channel *new_channel(struct peer *peer UNNEEDED, u64 dbid UNNEEDED, struct amount_sat our_funds UNNEEDED, bool remote_channel_ready UNNEEDED, /* NULL or stolen */ - struct short_channel_id *scid STEALS UNNEEDED, + struct short_channel_id *scid TAKES UNNEEDED, struct short_channel_id *old_scids TAKES UNNEEDED, struct short_channel_id alias_local UNNEEDED, struct short_channel_id *alias_remote STEALS UNNEEDED,