From 1e5789d421001af499b5df42a3a6f9fa1597070b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 12 Oct 2020 16:03:51 +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 | 22 ++++++++++++++++++++- 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(+), 6 deletions(-) diff --git a/doc/lightning-close.7 b/doc/lightning-close.7 index 769f1f987..405441ad1 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 3e5c6b09f..411493cc1 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 992bd6916..791e10129 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 91f70e7ea..b47f45f66 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 742f5c174..02870b4f3 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -4,13 +4,14 @@ from pyln.client import RpcError from shutil import copyfile from utils import ( only_one, sync_blockheight, wait_for, DEVELOPER, TIMEOUT, - account_balance, first_channel_id, basic_fee + account_balance, first_channel_id, basic_fee, TEST_NETWORK, ) import os import queue import pytest import re +import subprocess import threading import unittest @@ -126,6 +127,25 @@ 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) + + 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 e44b3cea7..0c44a217a 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1660,4 +1660,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:17fb4d8f180303fb57e28b5b5ac5a9a326826297933461030e9c4f85e02723f9 +// SHA256STAMP:8a260050ced7606fcad6e15df51a42a442f3119ad82d8e86fa2d348a2a45ee1a diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 2d189cca5..46635bcdf 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1660,4 +1660,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:17fb4d8f180303fb57e28b5b5ac5a9a326826297933461030e9c4f85e02723f9 +// SHA256STAMP:8a260050ced7606fcad6e15df51a42a442f3119ad82d8e86fa2d348a2a45ee1a diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index 1a372a54c..e806d2d42 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1090,7 +1090,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:13b6fd428b7832d7122e64b76c7833788472fdaacf521ef9cb2d0890f65b8db8 +# SHA256STAMP:9d0158f039940ef277ed6fc3a27b3695dec7f9869ea11e0f9eea8313a5849f08 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 74cd0aacc..8566d1787 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(); }