From 19989f0c021d294bb3b8217bf0cfb89f4585e57d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 12 Oct 2020 14:31:00 +1030 Subject: [PATCH] close: add notification for slow closes. For compatibility, we only do this if `allow-deprecated-apis` is false for now. Otherwise scripts parsing should use `grep -v '^# '` or start using `-N none`. Changelog-Added: JSON-RPC: `close` now sends notifications for slow closes (if `allow-deprecated-apis`=false) Changelog-Deprecated: cli: scripts should filter out '^# ' or use `-N none`, as commands will start returning notifications soon Fixes: #3925 Signed-off-by: Rusty Russell --- doc/lightning-close.7 | 8 +++++++- doc/lightning-close.7.md | 6 ++++++ lightningd/peer_control.c | 11 +++++++++++ lightningd/test/run-invoice-select-inchan.c | 6 ++++++ tests/test_closing.py | 21 +++++++++++++++++++++ wallet/db_postgres_sqlgen.c | 2 +- wallet/db_sqlite3_sqlgen.c | 2 +- wallet/statements_gettextgen.po | 4 ++-- wallet/test/run-wallet.c | 6 ++++++ 9 files changed, 61 insertions(+), 5 deletions(-) diff --git a/doc/lightning-close.7 b/doc/lightning-close.7 index 769f1f987d03..405441ad1f13 100644 --- a/doc/lightning-close.7 +++ b/doc/lightning-close.7 @@ -66,6 +66,12 @@ Prior to 0\.7\.2, \fBclose\fR took two parameters: \fIforce\fR and \fItimeout\fR an RPC error (default)\. Even after the timeout, the channel would be closed if the peer reconnected\. +.SH NOTIFICATIONS + +If \fBallow-deprecated-apis\fR is false, notifications may be returned +indicating what is going on, especially if the peer is offline and we +are waiting\. This will be enabled by default in future! + .SH RETURN VALUE On success, an object with fields \fItx\fR and \fItxid\fR containing the closing @@ -95,4 +101,4 @@ ZmnSCPxj \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:3763db99c82aebedf40e1ef39407f2970d0408601f9250ec9c52995956da621b +\" SHA256STAMP:d16e185f9a781f23987dfb65aaa1eae8dab19796975433c69e16f1f6b18751c5 diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 3e5c6b09fa8c..411493cc1ce0 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -57,6 +57,12 @@ Prior to 0.7.2, **close** took two parameters: *force* and *timeout*. an RPC error (default). Even after the timeout, the channel would be closed if the peer reconnected. +NOTIFICATIONS +------------- +If `allow-deprecated-apis` is false, notifications may be returned +indicating what is going on, especially if the peer is offline and we +are waiting. This will be enabled by default in future! + RETURN VALUE ------------ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 96d90f7b400b..8e040d98d451 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -284,6 +284,9 @@ close_command_timeout(struct close_command *cc) /* This will trigger drop_to_chain, which will trigger * resolution of the command and destruction of the * close_command. */ + if (!deprecated_apis) + json_notify_fmt(cc->cmd, LOG_INFORM, + "Timed out, forcing close."); channel_fail_permanent(cc->channel, "Forcibly closed by 'close' command timeout"); } @@ -306,6 +309,14 @@ register_close_command(struct lightningd *ld, tal_add_destructor2(channel, &destroy_close_command_on_channel_destroy, cc); + + if (!deprecated_apis && !channel->owner) { + char *msg = tal_strdup(tmpctx, "peer is offline, will negotiate once they reconnect"); + if (timeout) + tal_append_fmt(&msg, " (%u seconds before unilateral close)", + timeout); + json_notify_fmt(cmd, LOG_INFORM, "%s.", msg); + } log_debug(ld->log, "close_command: timeout = %u", timeout); if (timeout) new_reltimer(ld->timers, cc, time_from_sec(timeout), diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 6319f4e3506f..729b8ec8d10d 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -277,6 +277,12 @@ void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNN char *json_member_direct(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, size_t extra UNNEEDED) { fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_notify_fmt */ +void json_notify_fmt(struct command *cmd UNNEEDED, + enum log_level level UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "json_notify_fmt called!\n"); abort(); } /* Generated stub for json_object_end */ void json_object_end(struct json_stream *js UNNEEDED) { fprintf(stderr, "json_object_end called!\n"); abort(); } diff --git a/tests/test_closing.py b/tests/test_closing.py index 46f79c2d89e3..c426133b958d 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -11,6 +11,7 @@ import queue import pytest import re +import subprocess import threading import unittest @@ -126,6 +127,26 @@ def test_closing_while_disconnected(node_factory, bitcoind, executor): wait_for(lambda: len(l2.rpc.listchannels()['channels']) == 0) +def test_closing_disconnected_notify(node_factory, bitcoind, executor): + l1, l2 = node_factory.line_graph(2) + chan = l1.get_channel_scid(l2) + + l1.pay(l2, 200000000) + l2.stop() + wait_for(lambda: not only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']) + + out = subprocess.check_output(['cli/lightning-cli', + '--network={}'.format(TEST_NETWORK), + '--lightning-dir={}' + .format(l1.daemon.lightning_dir), + 'close', + l2.info['id'], + '5']).decode('utf-8').splitlines() + assert out[0] == '# peer is offline, will negotiate once they reconnect (5 seconds before unilateral close).' + assert out[1] == '# Timed out, forcing close.' + assert not any([line.startswith('#') for line in out[2:]]) + + def test_closing_id(node_factory): """Test closing using peer ID and full channel ID """ diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index bfc35e53645d..253852d710cd 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1654,4 +1654,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:b58d5903df88487b84515e511c1cd98e5fb15f86cf9676e3125ab56a27c27877 +// SHA256STAMP:12d7b4e87a8f236c61e5d123d9f66e875aba51ab81eec3887245e162d9a4d779 diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 4b7d3f2d7be5..ec2bb5f2f88b 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1654,4 +1654,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:b58d5903df88487b84515e511c1cd98e5fb15f86cf9676e3125ab56a27c27877 +// SHA256STAMP:12d7b4e87a8f236c61e5d123d9f66e875aba51ab81eec3887245e162d9a4d779 diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index aa8c9c38c93a..ed70cc419645 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1086,7 +1086,7 @@ msgstr "" msgid "not a valid SQL statement" msgstr "" -#: wallet/test/run-wallet.c:1359 +#: wallet/test/run-wallet.c:1365 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:b5142a6c1570a9fdf0745214134e54ff79e3d91408a91a1555cdb0a546fb0c7b +# SHA256STAMP:47bb209de73d3fe4ba484942a5e4a6d18b3cdd1d7eb5810609b11b6679077f62 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 20a405760d94..585f2b1c7cec 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -347,6 +347,12 @@ void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNN const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, const char *label UNNEEDED) { fprintf(stderr, "json_get_member called!\n"); abort(); } +/* Generated stub for json_notify_fmt */ +void json_notify_fmt(struct command *cmd UNNEEDED, + enum log_level level UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "json_notify_fmt called!\n"); abort(); } /* Generated stub for json_object_end */ void json_object_end(struct json_stream *js UNNEEDED) { fprintf(stderr, "json_object_end called!\n"); abort(); }