diff --git a/doc/developers-guide/plugin-development/event-notifications.md b/doc/developers-guide/plugin-development/event-notifications.md index cb26d8a16e82..249cba297d9c 100644 --- a/doc/developers-guide/plugin-development/event-notifications.md +++ b/doc/developers-guide/plugin-development/event-notifications.md @@ -519,3 +519,16 @@ In the shutdown case, plugins should not interact with lightnind except via (id- } } ``` + +### `channel_closed` + +A notification for topic `channel_closed` is sent when a channel has been successfully closed with a peer. It includes the peer id and the closing transaction ID. + +```json +{ + "channel_closed": { + "id": "0313ceef8b96120a48dbb8106e55b9aaa52787c376872ecc720bc4385c53df79d3", + "closing_txid": "0799d85107cb28cadbe4f9e12c3049b8175cda12dc399a870bf4b00d534d35f3" + } +} +``` diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index a10a1f32808c..cdaee41f0096 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -330,6 +331,13 @@ static void peer_closing_complete(struct channel *channel, const u8 *msg) REASON_UNKNOWN, "Closing complete"); + struct bitcoin_txid closing_txid; + bitcoin_txid(channel->last_tx, &closing_txid); + + /* Tell plugin about the channel close. */ + notify_channel_closed(channel->peer->ld, &channel->peer->id, + &closing_txid); + /* Channel gets dropped to chain cooperatively. */ drop_to_chain(channel->peer->ld, channel, true, NULL); } diff --git a/lightningd/notification.c b/lightningd/notification.c index 0a5a4d23cede..156499087af9 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -292,6 +292,26 @@ void notify_channel_opened(struct lightningd *ld, notify_send(ld, n); } +static void channel_closed_notification_serialize(struct json_stream *stream, + struct lightningd *ld, + const struct node_id *node_id, + const struct bitcoin_txid *closing_txid) +{ + json_add_node_id(stream, "id", node_id); + json_add_txid(stream, "closing_txid", closing_txid); +} + +REGISTER_NOTIFICATION(channel_closed) + +void notify_channel_closed(struct lightningd *ld, + const struct node_id *node_id, + const struct bitcoin_txid *closing_txid) +{ + struct jsonrpc_notification *n = notify_start("channel_closed"); + channel_closed_notification_serialize(n->stream, ld, node_id, closing_txid); + notify_send(ld, n); +} + static void channel_state_changed_notification_serialize(struct json_stream *stream, const struct node_id *peer_id, const struct channel_id *cid, diff --git a/lightningd/notification.h b/lightningd/notification.h index e7428c6afbac..8267bf39c787 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -50,6 +50,10 @@ void notify_channel_opened(struct lightningd *ld, const struct bitcoin_txid *funding_txid, bool channel_ready); +void notify_channel_closed(struct lightningd *ld, + const struct node_id *node_id, + const struct bitcoin_txid *closing_txid); + void notify_channel_state_changed(struct lightningd *ld, const struct node_id *peer_id, const struct channel_id *cid, diff --git a/tests/plugins/misc_notifications.py b/tests/plugins/misc_notifications.py index f11b4da0614a..8f7262b0fb47 100755 --- a/tests/plugins/misc_notifications.py +++ b/tests/plugins/misc_notifications.py @@ -22,6 +22,14 @@ def channel_opened(plugin, channel_opened, **kwargs): channel_opened["funding_txid"])) +@plugin.subscribe("channel_closed") +def channel_closed(plugin, channel_closed, **kwargs): + plugin.log( + "A channel was closed to us by {}, with a closing" + " transaction ID: {}".format(channel_closed["id"], + channel_closed["closing_txid"])) + + @plugin.subscribe("channel_state_changed") def channel_state_changed(plugin, channel_state_changed, **kwargs): plugin.log("channel_state_changed {}".format(channel_state_changed)) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 254cb956286d..d064edd2eab8 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1335,6 +1335,27 @@ def test_channel_opened_notification(node_factory): .format(l1.info["id"], amount)) +def test_channel_closed_notification(node_factory): + """ + Test the 'channel_closed' notification sent at channel closing success. + """ + plugin_path = os.path.join(os.getcwd(), "tests/plugins/misc_notifications.py") + opts = [{"plugin": plugin_path}, {"plugin": plugin_path}] + amount = 10**6 + l1, l2 = node_factory.line_graph(2, fundchannel=True, fundamount=amount, + opts=opts) + + closing_txid = only_one(l2.rpc.close(l1.info['id'])['txids']) + + l1.daemon.wait_for_log(r"A channel was closed to us by {}, with a closing" + " transaction ID: {}".format(l2.rpc.getinfo()["id"], + closing_txid,)) + + l2.daemon.wait_for_log(r"A channel was closed to us by {}, with a closing" + " transaction ID: {}".format(l1.rpc.getinfo()["id"], + closing_txid,)) + + def test_forward_event_notification(node_factory, bitcoind, executor): """ test 'forward_event' notifications """