From 40b264cf9597508027b2ca247d7e6eda517ede4b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:09:01 +0930 Subject: [PATCH 01/62] Makefile: fix $(FORCE) in sub-Makefiles It needs to be defined before we include them. Signed-off-by: Rusty Russell --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 646d90e5ce95..ac6fb7035152 100644 --- a/Makefile +++ b/Makefile @@ -258,6 +258,11 @@ endif default: show-flags all-programs all-test-programs doc-all +ifneq ($(SUPPRESS_GENERATION),1) +FORCE = FORCE +FORCE:: +endif + show-flags: config.vars @$(ECHO) "CC: $(CC) $(CFLAGS) -c -o" @$(ECHO) "LD: $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) -o" @@ -536,11 +541,6 @@ ncc: ${TARGET_DIR}/libwally-core-build/src/libwallycore.la TAGS: $(RM) TAGS; find * -name test -type d -prune -o -name '*.[ch]' -print -o -name '*.py' -print | xargs etags --append -ifneq ($(SUPPRESS_GENERATION),1) -FORCE = FORCE -FORCE:: -endif - ccan/ccan/cdump/tools/cdump-enumstr: ccan/ccan/cdump/tools/cdump-enumstr.o $(CDUMP_OBJS) $(CCAN_OBJS) ALL_PROGRAMS += ccan/ccan/cdump/tools/cdump-enumstr From bca8427317478d2310efb5df91f644cf5340cfe3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:10:01 +0930 Subject: [PATCH 02/62] Makefile: allow postfixes to SHA256STAMP. For markdown, there's no simple comment prefix: we need a postfix too. We also need to use "" since we want to use ' in some of the Makefiles in future when V=1. Signed-off-by: Rusty Russell --- Makefile | 14 +++++++------- doc/Makefile | 2 +- wallet/Makefile | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index ac6fb7035152..e56f81a4c636 100644 --- a/Makefile +++ b/Makefile @@ -293,30 +293,30 @@ else # Git doesn't maintain timestamps, so we only regen if sources actually changed: # We place the SHA inside some generated files so we can tell if they need updating. # Usage: $(call SHA256STAMP_CHANGED) -SHA256STAMP_CHANGED = [ x"`sed -n 's/.*SHA256STAMP://p' $@ 2>/dev/null`" != x"`cat $(sort $(filter-out FORCE,$^)) | $(SHA256SUM) | cut -c1-64`" ] -# Usage: $(call SHA256STAMP,commentprefix) -SHA256STAMP = echo '$(1) SHA256STAMP:'`cat $(sort $(filter-out FORCE,$^)) | $(SHA256SUM) | cut -c1-64` >> $@ +SHA256STAMP_CHANGED = [ x"`sed -n 's/.*SHA256STAMP:\([a-f0-9]*\).*/\1/p' $@ 2>/dev/null`" != x"`cat $(sort $(filter-out FORCE,$^)) | $(SHA256SUM) | cut -c1-64`" ] +# Usage: $(call SHA256STAMP,commentprefix,commentpostfix) +SHA256STAMP = echo "$(1) SHA256STAMP:"`cat $(sort $(filter-out FORCE,$^)) | $(SHA256SUM) | cut -c1-64`"$(2)" >> $@ endif # generate-wire.py --page [header|impl] hdrfilename wirename < csv > file %_wiregen.h: %_wire.csv $(WIRE_GEN_DEPS) @if $(call SHA256STAMP_CHANGED); then if [ "$$NO_PYTHON" = 1 ]; then echo "Error: NO_PYTHON on $@"; exit 1; fi; \ - $(call VERBOSE,"wiregen $@",tools/generate-wire.py --page header $($@_args) $@ `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//)); \ + $(call VERBOSE,"wiregen $@",tools/generate-wire.py --page header $($@_args) $@ `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//,)); \ fi %_wiregen.c: %_wire.csv $(WIRE_GEN_DEPS) @if $(call SHA256STAMP_CHANGED); then if [ "$$NO_PYTHON" = 1 ]; then echo "Error: NO_PYTHON on $@"; exit 1; fi; \ - $(call VERBOSE,"wiregen $@",tools/generate-wire.py --page impl $($@_args) ${@:.c=.h} `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//)); \ + $(call VERBOSE,"wiregen $@",tools/generate-wire.py --page impl $($@_args) ${@:.c=.h} `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//,)); \ fi %_printgen.h: %_wire.csv $(WIRE_GEN_DEPS) @if $(call SHA256STAMP_CHANGED); then if [ "$$NO_PYTHON" = 1 ]; then echo "Error: NO_PYTHON on $@"; exit 1; fi; \ - $(call VERBOSE,"printgen $@",tools/generate-wire.py -s -P --page header $($@_args) $@ `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//)); \ + $(call VERBOSE,"printgen $@",tools/generate-wire.py -s -P --page header $($@_args) $@ `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//,)); \ fi %_printgen.c: %_wire.csv $(WIRE_GEN_DEPS) @if $(call SHA256STAMP_CHANGED); then if [ "$$NO_PYTHON" = 1 ]; then echo "Error: NO_PYTHON on $@"; exit 1; fi; \ - $(call VERBOSE,"printgen $@",tools/generate-wire.py -s -P --page impl $($@_args) ${@:.c=.h} `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//)); \ + $(call VERBOSE,"printgen $@",tools/generate-wire.py -s -P --page impl $($@_args) ${@:.c=.h} `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//,)); \ fi include external/Makefile diff --git a/doc/Makefile b/doc/Makefile index 525c657774c9..a5cb7e2d73ff 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -87,7 +87,7 @@ MANPAGES := doc/lightning-cli.1 \ doc-all: $(MANPAGES) doc/index.rst $(MANPAGES): doc/%: doc/%.md - @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "mrkd $<", mrkd $< $@ && $(call SHA256STAMP,\")); else touch $@; fi + @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "mrkd $<", mrkd $< $@ && $(call SHA256STAMP,\\\",)); else touch $@; fi $(MANPAGES): $(FORCE) diff --git a/wallet/Makefile b/wallet/Makefile index 705290d7ae2b..a3bcda1da785 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -38,12 +38,12 @@ SQL_FILES := \ wallet/statements_gettextgen.po: $(SQL_FILES) $(FORCE) @if $(call SHA256STAMP_CHANGED); then if [ "$$NO_PYTHON" = 1 ]; then echo "Error: NO_PYTHON on $@"; exit 1; fi; \ - $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(SQL_FILES) && $(call SHA256STAMP,# )); \ + $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(SQL_FILES) && $(call SHA256STAMP,# ,)); \ fi wallet/db_%_sqlgen.c: wallet/statements_gettextgen.po devtools/sql-rewrite.py $(FORCE) @if $(call SHA256STAMP_CHANGED); then if [ "$$NO_PYTHON" = 1 ]; then echo "Error: NO_PYTHON on $@"; exit 1; fi; \ - $(call VERBOSE,"sql-rewrite $@",devtools/sql-rewrite.py wallet/statements_gettextgen.po $* > $@ && $(call SHA256STAMP,//)); \ + $(call VERBOSE,"sql-rewrite $@",devtools/sql-rewrite.py wallet/statements_gettextgen.po $* > $@ && $(call SHA256STAMP,//,)); \ fi maintainer-clean: wallet-maintainer-clean From ebe8f37400d0d877ce6a1f2051e3f3befc9b82a9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:11:01 +0930 Subject: [PATCH 03/62] doc: fix up mangled nroff from mkrd. Nested lists don't work (it puts the .RS at the end of the line instead of on a line by itself), so we sed it. https://github.com/refi64/mrkd/issues/4 This fixes up formatting on a number of existing pages.. Signed-off-by: Rusty Russell --- doc/Makefile | 4 +++- doc/lightning-getinfo.7 | 3 ++- doc/lightning-getlog.7 | 3 ++- doc/lightning-listconfigs.7 | 3 ++- doc/lightning-listnodes.7 | 3 ++- doc/lightning-listpeers.7 | 3 ++- doc/lightning-listtransactions.7 | 6 ++++-- 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index a5cb7e2d73ff..abc5bd35c678 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -86,8 +86,10 @@ MANPAGES := doc/lightning-cli.1 \ doc-all: $(MANPAGES) doc/index.rst +# mrkd doesn't format nested lists properly, so we fixup with sed (see doc/lightning-connect.7 +# and https://github.com/refi64/mrkd/issues/4 $(MANPAGES): doc/%: doc/%.md - @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "mrkd $<", mrkd $< $@ && $(call SHA256STAMP,\\\",)); else touch $@; fi + @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "mrkd $<", mrkd $< $@.tmp && sed -e 's/\(.\)\.RS$$/\1\n.RS/' < $@.tmp > $@ && rm $@.tmp && $(call SHA256STAMP,\\\",)); else touch $@; fi $(MANPAGES): $(FORCE) diff --git a/doc/lightning-getinfo.7 b/doc/lightning-getinfo.7 index 5d1bb242f748..92bab929a7a1 100644 --- a/doc/lightning-getinfo.7 +++ b/doc/lightning-getinfo.7 @@ -40,7 +40,8 @@ On success, an object with the following information is returned: .IP \[bu] \fInum_inactive_channels\fR: A integer that represents the number of channel which are closing\. .IP \[bu] -\fIaddress\fR: An array that represents all published addresses of the node, each object inside the array contains the following proprieties:.RS +\fIaddress\fR: An array that represents all published addresses of the node, each object inside the array contains the following proprieties: +.RS .IP \[bu] \fItype\fR: A string that represents the type of the address (currently \fBipv4\fR, \fBipv6\fR, \fBtorv3\fR or \fBtorv4\fR)\. .IP \[bu] diff --git a/doc/lightning-getlog.7 b/doc/lightning-getlog.7 index 003940c85ebf..13eedfb0471d 100644 --- a/doc/lightning-getlog.7 +++ b/doc/lightning-getlog.7 @@ -39,7 +39,8 @@ On success, a object will be return with the following parameters: .IP \[bu] \fIbytes_max\fR: An integer that represents the max dimension in bytes of log file\. .IP \[bu] -\fIlog\fR: An array of objects where each element contains the following proprieties:.RS +\fIlog\fR: An array of objects where each element contains the following proprieties: +.RS .IP \[bu] \fItype\fR: A string that represents the log level\. The propriety can have an value equal to SKIPPED to indicate the existence of omitted entries\. .IP \[bu] diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index d54182a7f5f9..771a04504ca7 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -35,7 +35,8 @@ Additional members include: .IP \[bu] \fI# version\fR: A string that represents the version of node\. .IP \[bu] -\fIplugins\fR: A array that represents the non-important plugin registered\. Each object contains the following members:.RS +\fIplugins\fR: A array that represents the non-important plugin registered\. Each object contains the following members: +.RS .IP \[bu] \fIpath\fR: A string that represents the path of plugin\. .IP \[bu] diff --git a/doc/lightning-listnodes.7 b/doc/lightning-listnodes.7 index 886638a5d8d1..a80ef7b13715 100644 --- a/doc/lightning-listnodes.7 +++ b/doc/lightning-listnodes.7 @@ -45,7 +45,8 @@ are also returned: .IP \[bu] \fIfeatures\fR: A string that represents the features value\. .IP \[bu] -\fIaddresses\fR: An array that represents the addreses avaible\. Each address is represented with an object with the following properties:.RS +\fIaddresses\fR: An array that represents the addreses avaible\. Each address is represented with an object with the following properties: +.RS .IP \[bu] \fItype\fR: A string that represents the address type (ipv4 or ipv6)\. .IP \[bu] diff --git a/doc/lightning-listpeers.7 b/doc/lightning-listpeers.7 index 1e312024079b..04ace1236a24 100644 --- a/doc/lightning-listpeers.7 +++ b/doc/lightning-listpeers.7 @@ -72,7 +72,8 @@ The objects in the \fIchannels\fR array will have at least these fields: .RS .IP \[bu] -\fIstate\fR: Any of these strings:.RS +\fIstate\fR: Any of these strings: +.RS .IP \[bu] \fB"OPENINGD"\fR: The channel funding protocol with the peer is ongoing and both sides are negotiating parameters\. diff --git a/doc/lightning-listtransactions.7 b/doc/lightning-listtransactions.7 index 9355dd788432..f87ff7b9c438 100644 --- a/doc/lightning-listtransactions.7 +++ b/doc/lightning-listtransactions.7 @@ -38,7 +38,8 @@ On success, the command will return a list of transactions, each object represen .IP \[bu] \fIversion\fR: An integer that represents the nVersion field\. .IP \[bu] -\fIinputs\fR: A list of spent transaction outputs, each spent transaction output is represented with an object with the following properties:.RS +\fIinputs\fR: A list of spent transaction outputs, each spent transaction output is represented with an object with the following properties: +.RS .IP \[bu] \fItxid\fR: A string that represents the hash of transaction\. This is the output index of the transaction output being spent\. .IP \[bu] @@ -49,7 +50,8 @@ On success, the command will return a list of transactions, each object represen .RE .IP \[bu] -\fIoutputs\fR: A list of transactions, each transaction is represented with an object with the following proprieties:.RS +\fIoutputs\fR: A list of transactions, each transaction is represented with an object with the following proprieties: +.RS .IP \[bu] \fIindex\fR: An integer that represents the index of transaction\. .IP \[bu] From 129d3f65e79126c0eabd76aa7c5bbb441b4009a0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:12:01 +0930 Subject: [PATCH 04/62] pytest: don't use command_success_str in test_libplugin.c result should *always* be an object. This allows it to add fields without breaking the API. A command which returns "result" as a string is living in sin. This changes one of the two callers of "command_success_str". Signed-off-by: Rusty Russell --- tests/plugins/test_libplugin.c | 2 +- tests/test_plugin.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/plugins/test_libplugin.c b/tests/plugins/test_libplugin.c index 6197116d8303..ef62ab96311b 100644 --- a/tests/plugins/test_libplugin.c +++ b/tests/plugins/test_libplugin.c @@ -22,7 +22,7 @@ static struct command_result *json_helloworld(struct command *cmd, if (!name) name = name_option ? name_option : tal_strdup(tmpctx, "world"); - return command_success_str(cmd, tal_fmt(tmpctx, "hello %s", name)); + return command_success(cmd, json_out_obj(cmd, "hello", name)); } static struct command_result * diff --git a/tests/test_plugin.py b/tests/test_plugin.py index cb3f6fe4d925..212eb7ccebf5 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1412,15 +1412,15 @@ def test_libplugin(node_factory): l1.rpc.check("helloworld") # Test commands - assert l1.rpc.call("helloworld") == "hello world" - assert l1.rpc.call("helloworld", {"name": "test"}) == "hello test" + assert l1.rpc.call("helloworld") == {"hello": "world"} + assert l1.rpc.call("helloworld", {"name": "test"}) == {"hello": "test"} l1.stop() l1.daemon.opts["plugin"] = plugin l1.daemon.opts["name"] = "test_opt" l1.start() - assert l1.rpc.call("helloworld") == "hello test_opt" + assert l1.rpc.call("helloworld") == {"hello": "test_opt"} # But param takes over! - assert l1.rpc.call("helloworld", {"name": "test"}) == "hello test" + assert l1.rpc.call("helloworld", {"name": "test"}) == {"hello": "test"} # Test hooks and notifications l2 = node_factory.get_node() @@ -1460,7 +1460,7 @@ def test_libplugin_deprecated(node_factory): 'name-deprecated': 'test_opt depr', 'allow-deprecated-apis': True}) - assert l1.rpc.call("helloworld") == "hello test_opt depr" + assert l1.rpc.call("helloworld") == {"hello": "test_opt depr"} l1.rpc.help('testrpc-deprecated') assert l1.rpc.call("testrpc-deprecated") == l1.rpc.getinfo() From 97b52ed8c3869116cbb7e2835a8df6f2a296f333 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:13:01 +0930 Subject: [PATCH 05/62] autoclean: don't return a raw string as result. This is hard to parse, and not extensible in future, and disagrees with the man page (and caught by schema). Technically this is an API break, but it can't be done neatly anyway and it's unlikely someone is relying on this today :( Signed-off-by: Rusty Russell Changelog-Changed: JSONRPC: `autocleaninvoice` now returns an object, not a raw string. --- plugins/autoclean.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 21f0c8d30c58..3b80815b0a47 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -39,6 +39,7 @@ static struct command_result *json_autocleaninvoice(struct command *cmd, { u64 *cycle; u64 *exby; + struct json_stream *response; if (!param(cmd, buffer, params, p_opt_def("cycle_seconds", param_u64, &cycle, 3600), @@ -50,18 +51,19 @@ static struct command_result *json_autocleaninvoice(struct command *cmd, expired_by = *exby; if (cycle_seconds == 0) { - tal_free(cleantimer); - return command_success_str(cmd, "Autoclean timer disabled"); + response = jsonrpc_stream_success(cmd); + json_add_bool(response, "enabled", false); + return command_finished(cmd, response); } tal_free(cleantimer); cleantimer = plugin_timer(cmd->plugin, time_from_sec(cycle_seconds), do_clean, cmd->plugin); - return command_success_str(cmd, - tal_fmt(cmd, "Autocleaning %"PRIu64 - "-second old invoices every %"PRIu64 - " seconds", - expired_by, cycle_seconds)); + response = jsonrpc_stream_success(cmd); + json_add_bool(response, "enabled", true); + json_add_u64(response, "cycle_seconds", cycle_seconds); + json_add_u64(response, "expired_by", expired_by); + return command_finished(cmd, response); } static const char *init(struct plugin *p, From a4c7ff0f10bc9e3157995c7dcf8abe5274c2c616 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:14:01 +0930 Subject: [PATCH 06/62] libplugin: remove command_success_str function. As mentioned in previous commits: "result" must be an object, and anything else is an antipattern. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 17 +---------------- plugins/libplugin.h | 5 ----- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index b522dbcaf75d..f28f5be722ed 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -345,21 +345,6 @@ command_success(struct command *cmd, const struct json_out *result) return command_complete(cmd, js); } -struct command_result *WARN_UNUSED_RESULT -command_success_str(struct command *cmd, const char *str) -{ - struct json_stream *js = jsonrpc_stream_start(cmd); - - if (str) - json_add_string(js, "result", str); - else { - /* Use an empty object if they don't want anything. */ - json_object_start(js, "result"); - json_object_end(js); - } - return command_complete(cmd, js); -} - struct command_result *command_done_err(struct command *cmd, errcode_t code, const char *errmsg, @@ -904,7 +889,7 @@ static struct command_result *handle_init(struct command *cmd, if (with_rpc) io_new_conn(p, p->rpc_conn->fd, rpc_conn_init, p); - return command_success_str(cmd, NULL); + return command_success(cmd, json_out_obj(cmd, NULL, NULL)); } char *u64_option(const char *arg, u64 *i) diff --git a/plugins/libplugin.h b/plugins/libplugin.h index a70fa8078b6f..6ae600fd9b90 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -181,11 +181,6 @@ struct command_result *command_err_raw(struct command *cmd, struct command_result *WARN_UNUSED_RESULT command_success(struct command *cmd, const struct json_out *result); -/* Simple version where we just want to send a string, or NULL means an empty - * result object. @cmd cannot be NULL. */ -struct command_result *WARN_UNUSED_RESULT -command_success_str(struct command *cmd, const char *str); - /* End a hook normally (with "result": "continue") */ struct command_result *WARN_UNUSED_RESULT command_hook_success(struct command *cmd); From fc9b24a7466ad829c1f5c98b4ef31ca17cfec177 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:15:01 +0930 Subject: [PATCH 07/62] close: add "unopened" type if we simply discard channel. Undocumented (caught by json schema!) if we discard channel because it wasn't open yet, then close returned the empty object. Make it return a new type in this case. Signed-off-by: Rusty Russell Changelog-Added: JSONRPC: `close` returns `type` "unopened" if it simply discards channel instead of empty object. --- lightningd/peer_control.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 93b31c17f045..f1ddbf6cbbfe 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1667,13 +1667,12 @@ static struct command_result *json_close(struct command *cmd, if (uc) { /* Easy case: peer can simply be forgotten. */ kill_uncommitted_channel(uc, "close command called"); - - return command_success(cmd, json_stream_success(cmd)); + goto discard_unopened; } if ((channel = peer_unsaved_channel(peer))) { channel_unsaved_close_conn(channel, "close command called"); - return command_success(cmd, json_stream_success(cmd)); + goto discard_unopened; } return command_fail(cmd, LIGHTNINGD, "Peer has no active channel"); @@ -1833,6 +1832,12 @@ static struct command_result *json_close(struct command *cmd, /* Wait until close drops down to chain. */ return command_still_pending(cmd); + +discard_unopened: { + struct json_stream *result = json_stream_success(cmd); + json_add_string(result, "type", "unopened"); + return command_success(cmd, result); + } } static const struct json_command close_command = { From 8a67c4a1ba8b39b327ff5304e035e347013d8a70 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:16:01 +0930 Subject: [PATCH 08/62] decode: always return "valid" field. Otherwise our schema is pretty meaningless, since invalid decodes can have missing "required" fields. Also fix a typo "blinded_payindo". Signed-off-by: Rusty Russell Changelog-Experimental: JSON-RPC: `decode` now gives a `valid` boolean (it does partial decodes of some invalid data). --- plugins/offers.c | 125 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 35 deletions(-) diff --git a/plugins/offers.c b/plugins/offers.c index 5bf5a5da1a42..fd98b2e13570 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -224,7 +224,8 @@ static void json_add_onionmsg_path(struct json_stream *js, json_object_end(js); } -static void json_add_blinded_paths(struct json_stream *js, +/* Returns true if valid */ +static bool json_add_blinded_paths(struct json_stream *js, struct blinded_path **paths, struct blinded_payinfo **blindedpay) { @@ -250,9 +251,13 @@ static void json_add_blinded_paths(struct json_stream *js, * exactly as many `payinfo` as total `onionmsg_path` in * `blinded_path`. */ - if (blindedpay && n != tal_count(blindedpay)) + if (blindedpay && n != tal_count(blindedpay)) { json_add_string(js, "warning_invoice_invalid_blinded_payinfo", "invoice does not have correct number of blinded_payinfo"); + return false; + } + + return true; } static const char *recurrence_time_unit_name(u8 time_unit) @@ -276,6 +281,7 @@ static const char *recurrence_time_unit_name(u8 time_unit) static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer) { struct sha256 offer_id; + bool valid = true; merkle_tlv(offer->fields, &offer_id); json_add_sha256(js, "offer_id", &offer_id); @@ -312,9 +318,11 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer json_add_stringn(js, "description", offer->description, tal_bytelen(offer->description)); - else + else { json_add_string(js, "warning_offer_missing_description", "offers without a description are invalid"); + valid = false; + } if (offer->vendor) json_add_stringn(js, "vendor", offer->vendor, @@ -325,7 +333,7 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer json_add_u64(js, "absolute_expiry", *offer->absolute_expiry); if (offer->paths) - json_add_blinded_paths(js, offer->paths, NULL); + valid &= json_add_blinded_paths(js, offer->paths, NULL); if (offer->quantity_min) json_add_u64(js, "quantity_min", *offer->quantity_min); @@ -363,9 +371,12 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer /* offer_decode fails if node_id or signature not set */ json_add_pubkey32(js, "node_id", offer->node_id); json_add_bip340sig(js, "signature", offer->signature); + + json_add_bool(js, "valid", valid); } -static void json_add_fallback_address(struct json_stream *js, +/* Returns true if valid */ +static bool json_add_fallback_address(struct json_stream *js, const struct chainparams *chain, u8 version, const u8 *address) { @@ -373,19 +384,23 @@ static void json_add_fallback_address(struct json_stream *js, /* Does extra checks, in particular checks v0 sizes */ if (segwit_addr_encode(out, chain->bip173_name, version, - address, tal_bytelen(address))) + address, tal_bytelen(address))) { json_add_string(js, "address", out); - else - json_add_string(js, - "warning_invoice_fallbacks_address_invalid", - "invalid fallback address for this version"); + return true; + } + json_add_string(js, + "warning_invoice_fallbacks_address_invalid", + "invalid fallback address for this version"); + return false; } -static void json_add_fallbacks(struct json_stream *js, +/* Returns true if valid */ +static bool json_add_fallbacks(struct json_stream *js, const struct bitcoin_blkid *chains, struct fallback_address **fallbacks) { const struct chainparams *chain; + bool valid = true; /* Present address as first chain mentioned. */ if (tal_count(chains) != 0) @@ -414,23 +429,29 @@ static void json_add_fallbacks(struct json_stream *js, json_add_string(js, "warning_invoice_fallbacks_version_invalid", "invoice fallback version > 16"); + valid = false; } else if (addrlen < 2 || addrlen > 40) { json_add_string(js, "warning_invoice_fallbacks_address_invalid", "invoice fallback address bad length"); + valid = false; } else if (chain) { - json_add_fallback_address(js, chain, - fallbacks[i]->version, - fallbacks[i]->address); + valid &= json_add_fallback_address(js, chain, + fallbacks[i]->version, + fallbacks[i]->address); } json_object_end(js); } json_array_end(js); + + return valid; } static void json_add_b12_invoice(struct json_stream *js, const struct tlv_invoice *invoice) { + bool valid = true; + if (invoice->chains) json_add_chains(js, invoice->chains); if (invoice->offer_id) @@ -442,9 +463,11 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->amount) json_add_amount_msat_only(js, "amount_msat", amount_msat(*invoice->amount)); - else + else { json_add_string(js, "warning_invoice_missing_amount", "invoices without an amount are invalid"); + valid = false; + } /* BOLT-offers #12: * - MUST reject the invoice if `description` is not present. @@ -452,9 +475,12 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->description) json_add_stringn(js, "description", invoice->description, tal_bytelen(invoice->description)); - else + else { json_add_string(js, "warning_invoice_missing_description", "invoices without a description are invalid"); + valid = false; + } + if (invoice->vendor) json_add_stringn(js, "vendor", invoice->vendor, tal_bytelen(invoice->vendor)); @@ -469,10 +495,12 @@ static void json_add_b12_invoice(struct json_stream *js, * contain exactly as many `payinfo` as total `onionmsg_path` * in `blinded_path`. */ - if (!invoice->blindedpay) + if (!invoice->blindedpay) { json_add_string(js, "warning_invoice_missing_blinded_payinfo", - "invoices with blinded_path without blinded_payindo are invalid"); - json_add_blinded_paths(js, invoice->paths, invoice->blindedpay); + "invoices with blinded_path without blinded_payinfo are invalid"); + valid = false; + } + valid &= json_add_blinded_paths(js, invoice->paths, invoice->blindedpay); } if (invoice->quantity) json_add_u64(js, "quantity", *invoice->quantity); @@ -494,9 +522,11 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->recurrence_basetime) json_add_u64(js, "recurrence_basetime", *invoice->recurrence_basetime); - else + else { json_add_string(js, "warning_invoice_missing_recurrence_basetime", "recurring invoices without a recurrence_basetime are invalid"); + valid = false; + } } if (invoice->payer_key) @@ -509,18 +539,22 @@ static void json_add_b12_invoice(struct json_stream *js, */ if (invoice->timestamp) json_add_u64(js, "timestamp", *invoice->timestamp); - else + else { json_add_string(js, "warning_invoice_missing_timestamp", "invoices without a timestamp are invalid"); + valid = false; + } /* BOLT-offers #12: * - MUST reject the invoice if `payment_hash` is not present. */ if (invoice->payment_hash) json_add_sha256(js, "payment_hash", invoice->payment_hash); - else + else { json_add_string(js, "warning_invoice_missing_payment_hash", "invoices without a payment_hash are invalid"); + valid = false; + } /* BOLT-offers #12: * @@ -544,8 +578,8 @@ static void json_add_b12_invoice(struct json_stream *js, json_add_u32(js, "min_final_cltv_expiry", 18); if (invoice->fallbacks) - json_add_fallbacks(js, invoice->chains, - invoice->fallbacks->fallbacks); + valid &= json_add_fallbacks(js, invoice->chains, + invoice->fallbacks->fallbacks); /* BOLT-offers #12: * - if the offer contained `refund_for`: @@ -560,28 +594,37 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->refund_signature) { json_add_bip340sig(js, "refund_signature", invoice->refund_signature); - if (!invoice->payer_key) + if (!invoice->payer_key) { json_add_string(js, "warning_invoice_refund_signature_missing_payer_key", "Can't have refund_signature without payer key"); - else if (!bolt12_check_signature(invoice->fields, - "invoice", - "refund_signature", - invoice->payer_key, - invoice->refund_signature)) + valid = false; + } else if (!bolt12_check_signature(invoice->fields, + "invoice", + "refund_signature", + invoice->payer_key, + invoice->refund_signature)) { json_add_string(js, "warning_invoice_refund_signature_invalid", "refund_signature does not match"); - } else if (invoice->refund_for) + valid = false; + } + } else if (invoice->refund_for) { json_add_string(js, "warning_invoice_refund_missing_signature", "refund_for requires refund_signature"); + valid = false; + } /* invoice_decode checked these */ json_add_pubkey32(js, "node_id", invoice->node_id); json_add_bip340sig(js, "signature", invoice->signature); + + json_add_bool(js, "valid", valid); } static void json_add_invoice_request(struct json_stream *js, const struct tlv_invoice_request *invreq) { + bool valid = true; + if (invreq->chains) json_add_chains(js, invreq->chains); /* BOLT-offers #12: @@ -592,9 +635,11 @@ static void json_add_invoice_request(struct json_stream *js, */ if (invreq->offer_id) json_add_sha256(js, "offer_id", invreq->offer_id); - else + else { json_add_string(js, "warning_invoice_request_missing_offer_id", "invoice_request requires offer_id"); + valid = false; + } if (invreq->amount) json_add_amount_msat_only(js, "amount_msat", amount_msat(*invreq->amount)); @@ -611,9 +656,11 @@ static void json_add_invoice_request(struct json_stream *js, *invreq->recurrence_start); if (invreq->payer_key) json_add_pubkey32(js, "payer_key", invreq->payer_key); - else + else { json_add_string(js, "warning_invoice_request_missing_payer_key", "invoice_request requires payer_key"); + valid = false; + } if (invreq->payer_info) json_add_hex_talarr(js, "payer_info", invreq->payer_info); @@ -631,13 +678,18 @@ static void json_add_invoice_request(struct json_stream *js, "invoice_request", "recurrence_signature", invreq->payer_key, - invreq->recurrence_signature)) + invreq->recurrence_signature)) { json_add_string(js, "warning_invoice_request_invalid_recurrence_signature", "Bad recurrence_signature"); + valid = false; + } } else if (invreq->recurrence_counter) { json_add_string(js, "warning_invoice_request_missing_recurrence_signature", "invoice_request requires recurrence_signature"); + valid = false; } + + json_add_bool(js, "valid", valid); } static struct command_result *json_decode(struct command *cmd, @@ -660,8 +712,11 @@ static struct command_result *json_decode(struct command *cmd, json_add_invoice_request(response, decodable->invreq); if (decodable->invoice) json_add_b12_invoice(response, decodable->invoice); - if (decodable->b11) + if (decodable->b11) { + /* The bolt11 decoder simply refuses to decode bad invs. */ json_add_bolt11(response, decodable->b11); + json_add_bool(response, "valid", true); + } return command_finished(cmd, response); } From ea99a05249249fbd77272e141bc3bb55fba28386 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:17:01 +0930 Subject: [PATCH 09/62] pytest: add schema support for JSON responses. This adds our first (basic) schema, and sews support into pyln-testing so it will load schemas for any method for doc/schemas/{method}.schema.json. All JSON responses in a test run are checked against the schema (if any). Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 1 + contrib/pyln-testing/pyln/testing/fixtures.py | 144 +++++++++++++++++- contrib/pyln-testing/pyln/testing/utils.py | 21 ++- contrib/pyln-testing/requirements.txt | 1 + doc/schemas/listpays.schema.json | 72 +++++++++ tests/fixtures.py | 2 +- 6 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 doc/schemas/listpays.schema.json diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 74858285ce4b..f4e53f31935d 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -349,6 +349,7 @@ def call(self, method, payload=None): "enable": True }, }) + # FIXME: Notification schema support? _, buf = self._readobj(sock, buf) request = { diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index 6f1d96b0e482..d4e25ffaf066 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -1,13 +1,17 @@ from concurrent import futures from pyln.testing.db import SqliteDbProvider, PostgresDbProvider from pyln.testing.utils import NodeFactory, BitcoinD, ElementsD, env, DEVELOPER, LightningNode, TEST_DEBUG, Throttler +from pyln.client import Millisatoshi from typing import Dict +import json +import jsonschema # type: ignore import logging import os import pytest # type: ignore import re import shutil +import string import sys import tempfile @@ -202,8 +206,145 @@ def throttler(test_base_dir): yield Throttler(test_base_dir) +def _extra_validator(): + """JSON Schema validator with additions for our specialized types""" + def is_hex(checker, instance): + """Hex string""" + if not checker.is_type(instance, "string"): + return False + return all(c in string.hexdigits for c in instance) + + def is_u64(checker, instance): + """64-bit integer""" + if not checker.is_type(instance, "integer"): + return False + return instance >= 0 and instance < 2**64 + + def is_u32(checker, instance): + """32-bit integer""" + if not checker.is_type(instance, "integer"): + return False + return instance >= 0 and instance < 2**32 + + def is_u16(checker, instance): + """16-bit integer""" + if not checker.is_type(instance, "integer"): + return False + return instance >= 0 and instance < 2**16 + + def is_short_channel_id(checker, instance): + """Short channel id""" + if not checker.is_type(instance, "string"): + return False + parts = instance.split("x") + if len(parts) != 3: + return False + # May not be integers + try: + blocknum = int(parts[0]) + txnum = int(parts[1]) + outnum = int(parts[2]) + except ValueError: + return False + + # BOLT #7: + # ## Definition of `short_channel_id` + # + # The `short_channel_id` is the unique description of the funding transaction. + # It is constructed as follows: + # 1. the most significant 3 bytes: indicating the block height + # 2. the next 3 bytes: indicating the transaction index within the block + # 3. the least significant 2 bytes: indicating the output index that pays to the + # channel. + return (blocknum >= 0 and blocknum < 2**24 + and txnum >= 0 and txnum < 2**24 + and outnum >= 0 and outnum < 2**16) + + def is_pubkey(checker, instance): + """SEC1 encoded compressed pubkey""" + if not checker.is_type(instance, "hex"): + return False + if len(instance) != 66: + return False + return instance[0:2] == "02" or instance[0:2] == "03" + + def is_pubkey32(checker, instance): + """x-only BIP-340 public key""" + if not checker.is_type(instance, "hex"): + return False + if len(instance) != 64: + return False + return True + + def is_signature(checker, instance): + """DER encoded secp256k1 ECDSA signature""" + if not checker.is_type(instance, "hex"): + return False + if len(instance) > 72 * 2: + return False + return True + + def is_bip340sig(checker, instance): + """Hex encoded secp256k1 Schnorr signature""" + if not checker.is_type(instance, "hex"): + return False + if len(instance) != 64 * 2: + return False + return True + + def is_msat(checker, instance): + """String number ending in msat""" + return type(instance) is Millisatoshi + + def is_txid(checker, instance): + """Bitcoin transaction ID""" + if not checker.is_type(instance, "hex"): + return False + return len(instance) == 64 + + type_checker = jsonschema.Draft7Validator.TYPE_CHECKER.redefine_many({ + "hex": is_hex, + "u64": is_u64, + "u32": is_u32, + "u16": is_u16, + "pubkey": is_pubkey, + "msat": is_msat, + "txid": is_txid, + "signature": is_signature, + "bip340sig": is_bip340sig, + "pubkey32": is_pubkey32, + "short_channel_id": is_short_channel_id, + }) + + return jsonschema.validators.extend(jsonschema.Draft7Validator, + type_checker=type_checker) + + +def _load_schema(filename): + """Load the schema from @filename and create a validator for it""" + with open(filename, 'r') as f: + return _extra_validator()(json.load(f)) + + +@pytest.fixture(autouse=True) +def jsonschemas(): + """Load schema files if they exist""" + try: + schemafiles = os.listdir('doc/schemas') + except FileNotFoundError: + schemafiles = [] + + schemas = {} + for fname in schemafiles: + if not fname.endswith('.schema.json'): + continue + schemas[fname.rpartition('.schema')[0]] = _load_schema(os.path.join('doc/schemas', + fname)) + return schemas + + @pytest.fixture -def node_factory(request, directory, test_name, bitcoind, executor, db_provider, teardown_checks, node_cls, throttler): +def node_factory(request, directory, test_name, bitcoind, executor, db_provider, teardown_checks, node_cls, throttler, jsonschemas): nf = NodeFactory( request, test_name, @@ -213,6 +354,7 @@ def node_factory(request, directory, test_name, bitcoind, executor, db_provider, db_provider=db_provider, node_cls=node_cls, throttler=throttler, + jsonschemas=jsonschemas, ) yield nf diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index fbc6a26317b7..3fb3c7eaa67b 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -601,7 +601,17 @@ class PrettyPrintingLightningRpc(LightningRpc): eyes. It has some overhead since we re-serialize the request and result to json in order to pretty print it. + Also validates (optional) schemas for us. """ + def __init__(self, socket_path, executor=None, logger=logging, + patch_json=True, jsonschemas={}): + super().__init__( + socket_path, + executor, + logger, + patch_json, + ) + self.jsonschemas = jsonschemas def call(self, method, payload=None): id = self.next_id @@ -615,6 +625,10 @@ def call(self, method, payload=None): "id": id, "result": res }, indent=2)) + + if method in self.jsonschemas: + self.jsonschemas[method].validate(res) + return res @@ -625,6 +639,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai allow_warning=False, allow_bad_gossip=False, db=None, port=None, disconnect=None, random_hsm=None, options=None, + jsonschemas={}, **kwargs): self.bitcoin = bitcoind self.executor = executor @@ -639,7 +654,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai self.rc = 0 socket_path = os.path.join(lightning_dir, TEST_NETWORK, "lightning-rpc").format(node_id) - self.rpc = PrettyPrintingLightningRpc(socket_path, self.executor) + self.rpc = PrettyPrintingLightningRpc(socket_path, self.executor, jsonschemas=jsonschemas) self.daemon = LightningD( lightning_dir, bitcoindproxy=bitcoind.get_proxy(), @@ -1196,7 +1211,7 @@ class NodeFactory(object): """A factory to setup and start `lightningd` daemons. """ def __init__(self, request, testname, bitcoind, executor, directory, - db_provider, node_cls, throttler): + db_provider, node_cls, throttler, jsonschemas): if request.node.get_closest_marker("slow_test") and SLOW_MACHINE: self.valgrind = False else: @@ -1211,6 +1226,7 @@ def __init__(self, request, testname, bitcoind, executor, directory, self.db_provider = db_provider self.node_cls = node_cls self.throttler = throttler + self.jsonschemas = jsonschemas def split_options(self, opts): """Split node options from cli options @@ -1289,6 +1305,7 @@ def get_node(self, node_id=None, options=None, dbfile=None, node = self.node_cls( node_id, lightning_dir, self.bitcoind, self.executor, self.valgrind, db=db, port=port, options=options, may_fail=may_fail or expect_fail, + jsonschemas=self.jsonschemas, **kwargs ) diff --git a/contrib/pyln-testing/requirements.txt b/contrib/pyln-testing/requirements.txt index 55031d6aa6d2..1b9fcc647929 100644 --- a/contrib/pyln-testing/requirements.txt +++ b/contrib/pyln-testing/requirements.txt @@ -9,3 +9,4 @@ pytest-timeout ~= 1.4.2 pytest-xdist ~= 2.2.0 pytest==6.1.* python-bitcoinlib==0.11.* +jsonschema==3.2.* diff --git a/doc/schemas/listpays.schema.json b/doc/schemas/listpays.schema.json new file mode 100644 index 000000000000..15d0e685b383 --- /dev/null +++ b/doc/schemas/listpays.schema.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "pays": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ "pending", "failed", "complete" ], + "description": "status of the payment" + }, + "destination": { + "type": "pubkey", + "description": "the final destination of the payment if known" + }, + "amount_msat": { + "type": "msat", + "description": "the amount the destination received, if known (**status** *complete* or *pending*)" + }, + "amount_sent_msat": { + "type": "msat", + "description": "the amount we actually sent, including fees (**status** *complete* or *pending*)" + }, + "created_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "preimage": { + "type": "hex", + "description": "proof of payment, only if (and always if) **status** is *complete*", + "FIXME": "we should enforce the status/payment_preimage relation in the schema!", + "maxLength": 64, + "minLength": 64 + }, + "label": { + "type": "string", + "description": "the label, if given to sendpay" + }, + "bolt11": { + "type": "string", + "description": "the bolt11 string (if pay supplied one)" + }, + "bolt12": { + "type": "string", + "description": "the bolt12 string (if supplied for pay: **experimental-offers** only)." + }, + "erroronion": { + "type": "hex", + "description": "the error onion returned on failure, if any." + }, + "number_of_parts": { + "type": "u64", + "description": "the number of parts for a successful payment (only if more than one, and **status** is *complete*)." + } + }, + "required": [ "payment_hash", "status", "created_at" ] + } + } + }, + "required": [ "pays" ] +} diff --git a/tests/fixtures.py b/tests/fixtures.py index c5a9aefadf17..e1fdc12388bf 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,5 +1,5 @@ from utils import DEVELOPER, TEST_NETWORK # noqa: F401,F403 -from pyln.testing.fixtures import directory, test_base_dir, test_name, chainparams, node_factory, bitcoind, teardown_checks, throttler, db_provider, executor, setup_logging # noqa: F401,F403 +from pyln.testing.fixtures import directory, test_base_dir, test_name, chainparams, node_factory, bitcoind, teardown_checks, throttler, db_provider, executor, setup_logging, jsonschemas # noqa: F401,F403 from pyln.testing import utils from utils import COMPAT From 2c9eaed294d319358e065a945b4443438bd5ce41 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:18:01 +0930 Subject: [PATCH 10/62] tools/fromschema.py: tool to replace start/end markers in markdown with schema. It can also be run standalone for debugging. Signed-off-by: Rusty Russell --- tools/fromschema.py | 247 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100755 tools/fromschema.py diff --git a/tools/fromschema.py b/tools/fromschema.py new file mode 100755 index 000000000000..a4d655336809 --- /dev/null +++ b/tools/fromschema.py @@ -0,0 +1,247 @@ +#! /usr/bin/env python3 +# Script to turn JSON schema into markdown documentation and replace in-place. +# Released by Rusty Russell under CC0: +# https://creativecommons.org/publicdomain/zero/1.0/ +from argparse import ArgumentParser +import json + + +def json_value(obj): + """Format obj in the JSON style for a value""" + if type(obj) is bool: + if obj: + return '*true*' + return '*false*' + if type(obj) is str: + return '"' + obj + '"' + assert False + + +def outputs(lines): + """Add these lines to the final output""" + print(''.join(lines), end='') + + +def output(line): + """Add this line to the final output""" + print(line, end='') + + +def output_type(properties, is_optional): + typename = properties['type'] + if typename == 'array': + typename += ' of {}s'.format(properties['items']['type']) + if is_optional: + typename += ", optional" + output(" ({})".format(typename)) + + +def output_range(properties): + if 'maximum' and 'minimum' in properties: + output(" ({} to {} inclusive)".format(properties['minimum'], + properties['maximum'])) + elif 'maximum' in properties: + output(" (max {})".format(properties['maximum'])) + elif 'minimum' in properties: + output(" (min {})".format(properties['minimum'])) + + if 'maxLength' and 'minLength' in properties: + if properties['minLength'] == properties['maxLength']: + output(' (always {} characters)'.format(properties['minLength'])) + else: + output(' ({} to {} characters)'.format(properties['minLength'], + properties['maxLength'])) + elif 'maxLength' in properties: + output(' (up to {} characters)'.format(properties['maxLength'])) + elif 'minLength' in properties: + output(' (at least {} characters)'.format(properties['minLength'])) + + if 'enum' in properties: + if len(properties['enum']) == 1: + output(" (always {})".format(json_value(properties['enum'][0]))) + else: + output(' (one of {})'.format(', '.join([json_value(p) for p in properties['enum']]))) + + +def output_member(propname, properties, is_optional, indent, print_type=True): + """Generate description line(s) for this member""" + output(indent + '- **{}**'.format(propname)) + if print_type: + output_type(properties, is_optional) + + if 'description' in properties: + output(": {}".format(properties['description'])) + + output_range(properties) + + if properties['type'] == 'object': + output(':\n') + output_members(properties, indent + ' ') + elif properties['type'] == 'array': + output(':\n') + output_array(properties['items'], indent + ' ') + else: + output('\n') + + +def output_array(items, indent): + """We've already said it's an array of {type}""" + if items['type'] == 'object': + output_members(items, indent) + elif items['type'] == 'array': + output(indent + '- {}:\n'.format(items['description'])) + output_array(items['items'], indent + ' ') + else: + output(indent + '- {}'.format(items['description'])) + output_range(items) + output('\n') + + +def output_members(sub, indent=''): + """Generate lines for these properties""" + warnings = [] + + # Remove deprecated and stub properties, collect warnings + # (Stubs required to keep additionalProperties: false happy) + for p in list(sub['properties'].keys()): + if len(sub['properties'][p]) == 0 or 'deprecated' in sub['properties'][p]: + del sub['properties'][p] + elif p.startswith('warning'): + warnings.append(p) + + # First list always-present properties + for p in sub['properties']: + if p.startswith('warning'): + continue + if p in sub['required']: + output_member(p, sub['properties'][p], False, indent) + + for p in sub['properties']: + if p.startswith('warning'): + continue + if p not in sub['required']: + output_member(p, sub['properties'][p], True, indent) + + if warnings != []: + output(indent + "- the following warnings are possible:\n") + for w in warnings: + output_member(w, sub['properties'][w], False, indent + ' ', print_type=False) + + # Not handled. + assert 'oneOf' not in sub + + # If we have multiple ifs, we have to wrap them in allOf. + if 'allOf' in sub: + ifclauses = sub['allOf'] + elif 'if' in sub: + ifclauses = [sub] + else: + ifclauses = [] + + # We partially handle if, assuming it depends on particular values of prior properties. + for ifclause in ifclauses: + conditions = [] + + # "required" are fields that simply must be present + for r in ifclause['if'].get('required', []): + conditions.append('**{}** is present'.format(r)) + + # "properties" are enums of field values + for tag, vals in ifclause['if'].get('properties', {}).items(): + # Don't have a description field here, it's not used. + assert 'description' not in vals + whichvalues = vals['enum'] + + cond = "**{}** is".format(tag) + if len(whichvalues) == 1: + cond += " {}".format(json_value(whichvalues[0])) + else: + cond += " {} or {}".format(", ".join([json_value(v) for v in whichvalues[:-1]]), + json_value(whichvalues[-1])) + conditions.append(cond) + + sentence = indent + "If " + ", and ".join(conditions) + ":\n" + # Prefix with blank line. + outputs(['\n', sentence]) + + output_members(ifclause['then'], indent + ' ') + + +def generate_from_schema(schema): + """This is not general, but works for us""" + assert schema['type'] == 'object' + + toplevels = [] + warnings = [] + props = schema['properties'] + + # We handle warnings on top-level objects with a separate section, + # so collect them now and remove them + for toplevel in list(props.keys()): + if toplevel.startswith('warning'): + warnings.append((toplevel, props[toplevel]['description'])) + del props[toplevel] + else: + toplevels.append(toplevel) + + # No properties -> empty object. + if toplevels == []: + output('On success, an empty object is returned.\n') + sub = schema + elif len(toplevels) == 1 and props[toplevels[0]]['type'] == 'object': + output('On success, an object containing **{}** is returned. It is an object containing:\n'.format(toplevels[0])) + # Don't have a description field here, it's not used. + assert 'description' not in toplevels[0] + sub = props[toplevels[0]] + elif len(toplevels) == 1 and props[toplevels[0]]['type'] == 'array': + output('On success, an object containing **{}** is returned. It is an array of objects, where each object contains:\n'.format(toplevels[0])) + # Don't have a description field here, it's not used. + assert 'description' not in toplevels[0] + sub = props[toplevels[0]]['items'] + else: + output('On success, an object is returned, containing:\n') + sub = schema + + output_members(sub) + + if warnings: + outputs(['\n', 'The following warnings may also be returned:\n']) + for w, desc in warnings: + output("- **{}**: {}\n".format(w, desc)) + + +def main(schemafile, markdownfile): + start_marker = '[comment]: # (GENERATE-FROM-SCHEMA-START)\n' + end_marker = '[comment]: # (GENERATE-FROM-SCHEMA-END)\n' + + if markdownfile is None: + with open(schemafile, "r") as f: + schema = json.load(f) + generate_from_schema(schema) + return + + with open(markdownfile, "r") as f: + md = f.readlines() + + suppress_output = False + for line in md: + if line == end_marker: + suppress_output = False + + if not suppress_output: + print(line, end='') + + if line == start_marker: + with open(schemafile, "r") as f: + schema = json.load(f) + generate_from_schema(schema) + suppress_output = True + + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument('schemafile', help='The schema file to use') + parser.add_argument('--markdownfile', help='The markdown file to read') + parsed_args = parser.parse_args() + + main(parsed_args.schemafile, parsed_args.markdownfile) From fdb4953fa965a3a8efafc48f8d0e194a65ebfae8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:19:01 +0930 Subject: [PATCH 11/62] doc/schemas: generate manpage section from schema. We start with the listpays manpage. Which is now complete! Signed-off-by: Rusty Russell --- doc/Makefile | 11 +++ doc/lightning-listpays.7 | 77 +++++++++---------- doc/lightning-listpays.7.md | 59 ++++++--------- doc/schemas/listpays.schema.json | 126 ++++++++++++++++++++++++------- 4 files changed, 170 insertions(+), 103 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index abc5bd35c678..30f4804d7168 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -86,12 +86,23 @@ MANPAGES := doc/lightning-cli.1 \ doc-all: $(MANPAGES) doc/index.rst +# Some manpages use a schema, so need that added. +MARKDOWN_WITH_SCHEMA := $(shell grep -l GENERATE-FROM-SCHEMA $(MANPAGES:=.md)) + +# These are hard to use in $(call) functions. +LBRACKET=( +RBRACKET=) + +$(MARKDOWN_WITH_SCHEMA): doc/lightning-%.7.md: doc/schemas/%.schema.json tools/fromschema.py + @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "fromschema $@", tools/fromschema.py --markdownfile=$@ doc/schemas/$*.schema.json > $@.tmp && grep -v SHA256STAMP: $@.tmp > $@ && rm -f $@.tmp && $(call SHA256STAMP,[comment]: # $(LBRACKET),$(RBRACKET))); else touch $@; fi + # mrkd doesn't format nested lists properly, so we fixup with sed (see doc/lightning-connect.7 # and https://github.com/refi64/mrkd/issues/4 $(MANPAGES): doc/%: doc/%.md @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "mrkd $<", mrkd $< $@.tmp && sed -e 's/\(.\)\.RS$$/\1\n.RS/' < $@.tmp > $@ && rm $@.tmp && $(call SHA256STAMP,\\\",)); else touch $@; fi $(MANPAGES): $(FORCE) +$(MARKDOWN_WITH_SCHEMA): $(FORCE) doc/protocol-%.svg: test/test_protocol test/test_protocol --svg < test/commits/$*.script > $@ diff --git a/doc/lightning-listpays.7 b/doc/lightning-listpays.7 index 8d0f0a0aa849..0d2c828a1f2d 100644 --- a/doc/lightning-listpays.7 +++ b/doc/lightning-listpays.7 @@ -12,56 +12,53 @@ single one if either \fIbolt11\fR or \fIpayment_hash\fR was specified\. .SH RETURN VALUE -On success, an array of objects is returned\. Each object contains: +On success, an object containing \fBpays\fR is returned\. It is an array of objects, where each object contains: +.RS +.IP \[bu] +\fBpayment_hash\fR (hex): the hash of the \fIpayment_preimage\fR which will prove payment (always 64 characters) +.IP \[bu] +\fBstatus\fR (string): status of the payment (one of "pending", "failed", "complete") +.IP \[bu] +\fBcreated_at\fR (u64): the UNIX timestamp showing when this payment was initiated +.IP \[bu] +\fBdestination\fR (pubkey, optional): the final destination of the payment if known +.IP \[bu] +\fBlabel\fR (string, optional): the label, if given to sendpay +.IP \[bu] +\fBbolt11\fR (string, optional): the bolt11 string (if pay supplied one) +.IP \[bu] +\fBbolt12\fR (string, optional): the bolt12 string (if supplied for pay: \fBexperimental-offers\fR only)\. - \fIbolt11\fR -the \fIbolt11\fR invoice if provided to \fBpay\fR\. +.RE +If \fBstatus\fR is "pending" or "complete": - \fIbolt12\fR -if \fBexperimental-offers\fR is enabled, and \fBpay\fR was a given a bolt12 -invoice, this field will appear instead of \fIbolt11\fR\. +.RS +.IP \[bu] +\fBamount_sent_msat\fR (msat): the amount we actually sent, including fees +.IP \[bu] +\fBamount_msat\fR (msat, optional): the amount the destination received, if known +.RE - \fIpayment_hash\fR -the \fIpayment_hash\fR of the payment\. +If \fBstatus\fR is "complete": +.RS +.IP \[bu] +\fBpreimage\fR (hex): proof of payment (always 64 characters) +.IP \[bu] +\fBnumber_of_parts\fR (u64, optional): the number of parts for a successful payment (only if more than one)\. - \fIstatus\fR -one of \fIcomplete\fR, \fIfailed\fR or \fIpending\fR\. +.RE +If \fBstatus\fR is "failed": - \fIpayment_preimage\fR -if \fIstatus\fR is \fIcomplete\fR\. - - - \fIlabel\fR -optional \fIlabel\fR, if provided to \fIpay\fR or \fIsendonion\fR\. - - - \fIamount_sent_msat\fR -total amount sent, in "NNNmsat" format\. - - -For old payments (pre-0\.7) we didn’t save the \fIbolt11\fR string, so in its -place are three other fields: - - - \fIpayment_hash\fR -the hash of the \fIpayment_preimage\fR which will prove payment\. - - - \fIdestination\fR -the final destination of the payment\. - - - \fIamount_msat\fR -the amount the destination received, in "NNNmsat" format\. - - -These three can all be extracted from \fIbolt11\fR, hence are obsolete\. +.RS +.IP \[bu] +\fBerroronion\fR (hex, optional): the error onion returned on failure, if any\. +.RE .SH AUTHOR Rusty Russell \fI is mainly responsible\. @@ -74,4 +71,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:5a0e016e18f52ce18484d064c3e659aca2687eeafca4b4365e3037faa1fba53f +\" SHA256STAMP:e27d57394bef9bdaf9b99ae0d9050c9044c194ab66f6c94c43b532a86e1a0031 diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index 87cab6a683b5..0ade89123bb0 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -15,43 +15,27 @@ single one if either *bolt11* or *payment_hash* was specified. RETURN VALUE ------------ -On success, an array of objects is returned. Each object contains: - - *bolt11* -the *bolt11* invoice if provided to `pay`. - - *bolt12* -if **experimental-offers** is enabled, and `pay` was a given a bolt12 -invoice, this field will appear instead of *bolt11*. - - *payment_hash* -the *payment_hash* of the payment. - - *status* -one of *complete*, *failed* or *pending*. - - *payment\_preimage* -if *status* is *complete*. - - *label* -optional *label*, if provided to *pay* or *sendonion*. - - *amount\_sent\_msat* -total amount sent, in "NNNmsat" format. - -For old payments (pre-0.7) we didn’t save the *bolt11* string, so in its -place are three other fields: - - *payment\_hash* -the hash of the *payment\_preimage* which will prove payment. - - *destination* -the final destination of the payment. - - *amount\_msat* -the amount the destination received, in "NNNmsat" format. - -These three can all be extracted from *bolt11*, hence are obsolete. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **pays** is returned. It is an array of objects, where each object contains: +- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **status** (string): status of the payment (one of "pending", "failed", "complete") +- **created_at** (u64): the UNIX timestamp showing when this payment was initiated +- **destination** (pubkey, optional): the final destination of the payment if known +- **label** (string, optional): the label, if given to sendpay +- **bolt11** (string, optional): the bolt11 string (if pay supplied one) +- **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). + +If **status** is "pending" or "complete": + - **amount_sent_msat** (msat): the amount we actually sent, including fees + - **amount_msat** (msat, optional): the amount the destination received, if known + +If **status** is "complete": + - **preimage** (hex): proof of payment (always 64 characters) + - **number_of_parts** (u64, optional): the number of parts for a successful payment (only if more than one). + +If **status** is "failed": + - **erroronion** (hex, optional): the error onion returned on failure, if any. +[comment]: # (GENERATE-FROM-SCHEMA-END) AUTHOR ------ @@ -68,3 +52,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:12e7b91fc59ee65b61d9aba4e8586fda8fbb524a7e548ffa36862e204952c46b) diff --git a/doc/schemas/listpays.schema.json b/doc/schemas/listpays.schema.json index 15d0e685b383..82c9e4548430 100644 --- a/doc/schemas/listpays.schema.json +++ b/doc/schemas/listpays.schema.json @@ -2,12 +2,14 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, + "required": [ "pays" ], "properties": { "pays": { "type": "array", "items": { "type": "object", - "additionalProperties": false, + "additionalProperties": true, + "required": [ "payment_hash", "status", "created_at" ], "properties": { "payment_hash": { "type": "hex", @@ -24,25 +26,10 @@ "type": "pubkey", "description": "the final destination of the payment if known" }, - "amount_msat": { - "type": "msat", - "description": "the amount the destination received, if known (**status** *complete* or *pending*)" - }, - "amount_sent_msat": { - "type": "msat", - "description": "the amount we actually sent, including fees (**status** *complete* or *pending*)" - }, "created_at": { "type": "u64", "description": "the UNIX timestamp showing when this payment was initiated" }, - "preimage": { - "type": "hex", - "description": "proof of payment, only if (and always if) **status** is *complete*", - "FIXME": "we should enforce the status/payment_preimage relation in the schema!", - "maxLength": 64, - "minLength": 64 - }, "label": { "type": "string", "description": "the label, if given to sendpay" @@ -54,19 +41,106 @@ "bolt12": { "type": "string", "description": "the bolt12 string (if supplied for pay: **experimental-offers** only)." + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ "pending", "complete" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "amount_sent_msat" ], + "properties": { + "payment_hash": { }, + "status": { }, + "destination": { }, + "created_at": { }, + "label": { }, + "bolt11": { }, + "bolt12": { }, + "preimage": { }, + "number_of_parts": { }, + "amount_msat": { + "type": "msat", + "description": "the amount the destination received, if known" + }, + "amount_sent_msat": { + "type": "msat", + "description": "the amount we actually sent, including fees" + } + } + } }, - "erroronion": { - "type": "hex", - "description": "the error onion returned on failure, if any." + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ "complete" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "preimage" ], + "properties": { + "payment_hash": { }, + "status": { }, + "destination": { }, + "created_at": { }, + "label": { }, + "bolt11": { }, + "bolt12": { }, + "amount_msat": { }, + "amount_sent_msat": { }, + "preimage": { + "type": "hex", + "description": "proof of payment", + "maxLength": 64, + "minLength": 64 + }, + "number_of_parts": { + "type": "u64", + "description": "the number of parts for a successful payment (only if more than one)." + } + } + } }, - "number_of_parts": { - "type": "u64", - "description": "the number of parts for a successful payment (only if more than one, and **status** is *complete*)." + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ "failed" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ ], + "properties": { + "payment_hash": { }, + "status": { }, + "destination": { }, + "created_at": { }, + "label": { }, + "bolt11": { }, + "bolt12": { }, + "erroronion": { + "type": "hex", + "description": "the error onion returned on failure, if any." + } + } + } } - }, - "required": [ "payment_hash", "status", "created_at" ] + ] } } - }, - "required": [ "pays" ] + } } From 3bc5d47aa2bb79c958497ef24e70181b07329a55 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:20:01 +0930 Subject: [PATCH 12/62] doc/schemas: add some simple schemas and generate manpages from them addgossip, check, createinvoice, createonion. Signed-off-by: Rusty Russell --- doc/lightning-addgossip.7 | 2 +- doc/lightning-addgossip.7.md | 3 ++ doc/lightning-check.7 | 10 ++-- doc/lightning-check.7.md | 7 ++- doc/lightning-createinvoice.7 | 38 +++++++++++++-- doc/lightning-createinvoice.7.md | 22 ++++++++- doc/lightning-createonion.7 | 24 ++++++++-- doc/lightning-createonion.7.md | 16 +++++-- doc/schemas/addgossip.schema.json | 7 +++ doc/schemas/check.schema.json | 12 +++++ doc/schemas/createinvoice.schema.json | 67 +++++++++++++++++++++++++++ doc/schemas/createonion.schema.json | 22 +++++++++ 12 files changed, 212 insertions(+), 18 deletions(-) create mode 100644 doc/schemas/addgossip.schema.json create mode 100644 doc/schemas/check.schema.json create mode 100644 doc/schemas/createinvoice.schema.json create mode 100644 doc/schemas/createonion.schema.json diff --git a/doc/lightning-addgossip.7 b/doc/lightning-addgossip.7 index adac5d48eb76..a050c3b3eb43 100644 --- a/doc/lightning-addgossip.7 +++ b/doc/lightning-addgossip.7 @@ -35,4 +35,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:40440e0a1159025d8fac68fc127029bf8539b103c0473d6bfaacd7cef5b56bea +\" SHA256STAMP:dd8b1fd2ffcf7c57629a042f44f2fbc526d8892a2c933de6e82511721a8ffdea diff --git a/doc/lightning-addgossip.7.md b/doc/lightning-addgossip.7.md index 1ff80f997f20..2bac1588465a 100644 --- a/doc/lightning-addgossip.7.md +++ b/doc/lightning-addgossip.7.md @@ -22,7 +22,9 @@ messages within error replies. RETURN VALUE ------------ +[comment]: # (GENERATE-FROM-SCHEMA-START) On success, an empty object is returned. +[comment]: # (GENERATE-FROM-SCHEMA-END) AUTHOR ------ @@ -39,3 +41,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:f974a3848c4db5b73fffa969a741ef6619c9a375783fabe731882d84a6bbf5ff) diff --git a/doc/lightning-check.7 b/doc/lightning-check.7 index abfa871a33f2..27dd0e436690 100644 --- a/doc/lightning-check.7 +++ b/doc/lightning-check.7 @@ -23,9 +23,13 @@ find a route even if checking the parameters succeeds\. .SH RETURN VALUE -On success, the \fIcommand_to_check\fR is returned\. On failure, the -relevant RPC error is returned\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBcommand_to_check\fR (string): the \fIcommand_to_check\fR argument + +.RE .SH AUTHOR Mark Beckwith \fI and Rusty Russell @@ -35,4 +39,4 @@ Mark Beckwith \fI and Rusty Russell Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:8ff7c586849eae5646f182b088351b4a5baa61e9e628a31763a6647e74a7fc0a +\" SHA256STAMP:9e78651117d3546edaf5150621630ee5dc4ccefd6e6a17b52b9dc8f86e8ba0c9 diff --git a/doc/lightning-check.7.md b/doc/lightning-check.7.md index a0303c75abd3..67bd9ecec8da 100644 --- a/doc/lightning-check.7.md +++ b/doc/lightning-check.7.md @@ -23,8 +23,10 @@ find a route even if checking the parameters succeeds. RETURN VALUE ------------ -On success, the *command\_to\_check* is returned. On failure, the -relevant RPC error is returned. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **command_to_check** (string): the *command_to_check* argument +[comment]: # (GENERATE-FROM-SCHEMA-END) AUTHOR ------ @@ -37,3 +39,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:5b399ee88a5fb6b7eac0e1ac349a68a8715154f8c6468aedf446c703c91ac165) diff --git a/doc/lightning-createinvoice.7 b/doc/lightning-createinvoice.7 index 11ef5c4a4d25..499537c23fcf 100644 --- a/doc/lightning-createinvoice.7 +++ b/doc/lightning-createinvoice.7 @@ -28,9 +28,41 @@ the invoice\. .SH RETURN VALUE -On success, an invoice object is returned, as per \fBlistinvoices\fR(7)\. +(Note: the return format is the same as \fBlightning-listinvoices\fR(7))\. +On success, an object is returned, containing: + +.RS +.IP \[bu] +\fBlabel\fR (string): the label for the invoice +.IP \[bu] +\fBpayment_hash\fR (hex): the hash of the \fIpayment_preimage\fR which will prove payment (always 64 characters) +.IP \[bu] +\fBstatus\fR (string): Whether it has been paid, or can no longer be paid (one of "paid", "expired", "unpaid") +.IP \[bu] +\fBdescription\fR (string): Description extracted from \fBbolt11\fR or \fBbolt12\fR +.IP \[bu] +\fBexpires_at\fR (u64): UNIX timestamp of when invoice expires (or expired) +.IP \[bu] +\fBbolt11\fR (string, optional): the bolt11 string (always present unless \fBbolt12\fR is) +.IP \[bu] +\fBbolt12\fR (string, optional): the bolt12 string instead of \fBbolt11\fR (\fBexperimental-offers\fR only) +.IP \[bu] +\fBamount_msat\fR (msat, optional): The amount of the invoice (if it has one) +.IP \[bu] +\fBpay_index\fR (u64, optional): Incrementing id for when this was paid (\fBstatus\fR \fIpaid\fR only) +.IP \[bu] +\fBamount_received_msat\fR (msat, optional): Amount actually received (\fBstatus\fR \fIpaid\fR only) +.IP \[bu] +\fBpaid_at\fR (u64, optional): UNIX timestamp of when invoice was paid (\fBstatus\fR \fIpaid\fR only) +.IP \[bu] +\fBpayment_preimage\fR (hex, optional): the proof of payment: SHA256 of this \fBpayment_hash\fR (always 64 characters) +.IP \[bu] +\fBlocal_offer_id\fR (hex, optional): the \fIid\fR of our offer which created this invoice (\fBexperimental-offers\fR only)\. (always 64 characters) + +.RE + On failure, an error is returned and no invoice is created\. If the lightning process fails before responding, the caller should use \fBlightning-listinvoices\fR(7) to query whether this invoice was created or @@ -53,10 +85,10 @@ Rusty Russell \fI is mainly responsible\. .SH SEE ALSO \fBlightning-invoice\fR(7), \fBlightning-listinvoices\fR(7), \fBlightning-delinvoice\fR(7), -\fBlightning-getroute\fR(7), \fBlightning-sendpay\fR(7)\. +\fBlightning-getroute\fR(7), \fBlightning-sendpay\fR(7), \fBlightning-offer\fR(7)\. .SH RESOURCES Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:155724c3e3130ed7f96d50a37dff99711dfcb4056c57d7eeb488fdb2b7925839 +\" SHA256STAMP:2d654675f966516a0fb0553f66e5bacd3ab96482d20cd9701a84e15ae49a4d92 diff --git a/doc/lightning-createinvoice.7.md b/doc/lightning-createinvoice.7.md index 1f5ee4d0ab37..99f5ce7ac859 100644 --- a/doc/lightning-createinvoice.7.md +++ b/doc/lightning-createinvoice.7.md @@ -28,7 +28,24 @@ the invoice. RETURN VALUE ------------ -On success, an invoice object is returned, as per listinvoices(7). +(Note: the return format is the same as lightning-listinvoices(7)). + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **label** (string): the label for the invoice +- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **status** (string): Whether it has been paid, or can no longer be paid (one of "paid", "expired", "unpaid") +- **description** (string): Description extracted from **bolt11** or **bolt12** +- **expires_at** (u64): UNIX timestamp of when invoice expires (or expired) +- **bolt11** (string, optional): the bolt11 string (always present unless **bolt12** is) +- **bolt12** (string, optional): the bolt12 string instead of **bolt11** (**experimental-offers** only) +- **amount_msat** (msat, optional): The amount of the invoice (if it has one) +- **pay_index** (u64, optional): Incrementing id for when this was paid (**status** *paid* only) +- **amount_received_msat** (msat, optional): Amount actually received (**status** *paid* only) +- **paid_at** (u64, optional): UNIX timestamp of when invoice was paid (**status** *paid* only) +- **payment_preimage** (hex, optional): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) +- **local_offer_id** (hex, optional): the *id* of our offer which created this invoice (**experimental-offers** only). (always 64 characters) +[comment]: # (GENERATE-FROM-SCHEMA-END) On failure, an error is returned and no invoice is created. If the lightning process fails before responding, the caller should use @@ -48,10 +65,11 @@ SEE ALSO -------- lightning-invoice(7), lightning-listinvoices(7), lightning-delinvoice(7), -lightning-getroute(7), lightning-sendpay(7). +lightning-getroute(7), lightning-sendpay(7), lightning-offer(7). RESOURCES --------- Main web site: +[comment]: # ( SHA256STAMP:95a2fbf9c94fa1e01a322658035473c694bfb93e02d32c2cefafe6ef5b676695) diff --git a/doc/lightning-createonion.7 b/doc/lightning-createonion.7 index 41a0a9dce284..2f075852f805 100644 --- a/doc/lightning-createonion.7 +++ b/doc/lightning-createonion.7 @@ -104,9 +104,25 @@ routing\. .SH RETURN VALUE -On success, an object containing the onion and the shared secrets will be -returned\. Otherwise an error will be reported\. The following example is the -result of calling \fIcreateonion\fR with the above hops parameter: +On success, an object is returned, containing: + +.RS +.IP \[bu] +\fBonion\fR (hex): the onion packet (\fIonion_size\fR bytes) +.IP \[bu] +\fBshared_secrets\fR (array of hexs): one shared secret for each node in the \fIhops\fR parameter: +.RS +.IP \[bu] +the shared secret with this hop (always 64 characters) + +.RE + + +.RE +.SH EXAMPLE + +The following example is the result of calling \fIcreateonion\fR with the +above hops parameter: .nf .RS @@ -137,4 +153,4 @@ Christian Decker \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:d32334049025248f8b6088afed4e3322be75815ea6b976f79a007c619518f98a +\" SHA256STAMP:c21aa197637bea17068072cd5907ad3302b48586067a8a5a8d748fd8e7e0a668 diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 630f327aa574..b10ecfce8dd0 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -92,9 +92,18 @@ routing. RETURN VALUE ------------ -On success, an object containing the onion and the shared secrets will be -returned. Otherwise an error will be reported. The following example is the -result of calling *createonion* with the above hops parameter: +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **onion** (hex): the onion packet (*onion_size* bytes) +- **shared_secrets** (array of hexs): one shared secret for each node in the *hops* parameter: + - the shared secret with this hop (always 64 characters) +[comment]: # (GENERATE-FROM-SCHEMA-END) + +EXAMPLE +------- + +The following example is the result of calling *createonion* with the +above hops parameter: ```json { @@ -126,3 +135,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md +[comment]: # ( SHA256STAMP:a5a64325f4232f27bccbbe1c9fc62bfb602ba60c81f46a1ef2df25b06dac807e) diff --git a/doc/schemas/addgossip.schema.json b/doc/schemas/addgossip.schema.json new file mode 100644 index 000000000000..b797a82c2f6f --- /dev/null +++ b/doc/schemas/addgossip.schema.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + } +} diff --git a/doc/schemas/check.schema.json b/doc/schemas/check.schema.json new file mode 100644 index 000000000000..faecc7d78a6a --- /dev/null +++ b/doc/schemas/check.schema.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + "command_to_check": { + "type": "string", + "description": "the *command_to_check* argument" + } + }, + "required": [ "command_to_check" ] +} diff --git a/doc/schemas/createinvoice.schema.json b/doc/schemas/createinvoice.schema.json new file mode 100644 index 000000000000..b1e6aa13107f --- /dev/null +++ b/doc/schemas/createinvoice.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "label", "payment_hash", "status", "description", "expires_at" ], + "properties": { + "label": { + "type": "string", + "description": "the label for the invoice" + }, + "bolt11": { + "type": "string", + "description": "the bolt11 string (always present unless **bolt12** is)" + }, + "bolt12": { + "type": "string", + "description": "the bolt12 string instead of **bolt11** (**experimental-offers** only)" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "amount_msat": { + "type": "msat", + "description": "The amount of the invoice (if it has one)" + }, + "status": { + "type": "string", + "enum": [ "paid", "expired", "unpaid" ], + "description": "Whether it has been paid, or can no longer be paid" + }, + "description": { + "type": "string", + "description": "Description extracted from **bolt11** or **bolt12**" + }, + "expires_at": { + "type": "u64", + "description": "UNIX timestamp of when invoice expires (or expired)" + }, + "pay_index": { + "type": "u64", + "description": "Incrementing id for when this was paid (**status** *paid* only)" + }, + "amount_received_msat": { + "type": "msat", + "description": "Amount actually received (**status** *paid* only)" + }, + "paid_at": { + "type": "u64", + "description": "UNIX timestamp of when invoice was paid (**status** *paid* only)" + }, + "payment_preimage": { + "type": "hex", + "description": "the proof of payment: SHA256 of this **payment_hash**", + "maxLength": 64, + "minLength": 64 + }, + "local_offer_id": { + "type": "hex", + "description": "the *id* of our offer which created this invoice (**experimental-offers** only).", + "maxLength": 64, + "minLength": 64 + } + } +} diff --git a/doc/schemas/createonion.schema.json b/doc/schemas/createonion.schema.json new file mode 100644 index 000000000000..7917163e18df --- /dev/null +++ b/doc/schemas/createonion.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "onion", "shared_secrets" ], + "properties": { + "onion": { + "type": "hex", + "description": "the onion packet (*onion_size* bytes)" + }, + "shared_secrets": { + "type": "array", + "description": "one shared secret for each node in the *hops* parameter", + "items": { + "type": "hex", + "description": "the shared secret with this hop", + "maxLength": 64, + "minLength": 64 + } + } + } +} From 0b48dd79ac2516f8bea51336d35cab87a87754a9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:21:01 +0930 Subject: [PATCH 13/62] doc/schemas: add schema for autoclean. Signed-off-by: Rusty Russell --- doc/lightning-autocleaninvoice.7 | 19 +++++++++-- doc/lightning-autocleaninvoice.7.md | 10 +++++- doc/schemas/autocleaninvoice.schema.json | 41 ++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 doc/schemas/autocleaninvoice.schema.json diff --git a/doc/lightning-autocleaninvoice.7 b/doc/lightning-autocleaninvoice.7 index fcbf233e4dc0..0c9dfead0e36 100644 --- a/doc/lightning-autocleaninvoice.7 +++ b/doc/lightning-autocleaninvoice.7 @@ -25,8 +25,23 @@ On startup of the daemon, no autoclean is set up\. .SH RETURN VALUE -On success, an empty object is returned\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBenabled\fR (boolean): whether invoice autocleaning is active + +.RE + +If \fBenabled\fR is \fItrue\fR: + +.RS +.IP \[bu] +\fBexpired_by\fR (u64): how long an invoice must be expired (seconds) before we delete it +.IP \[bu] +\fBcycle_seconds\fR (u64): how long an invoice must be expired (seconds) before we delete it + +.RE .SH AUTHOR ZmnSCPxj \fI is mainly responsible\. @@ -39,4 +54,4 @@ ZmnSCPxj \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:df05ece680710d67202b28af635a274d7adc38d9f334c79d88ee28b80a2cae60 +\" SHA256STAMP:dd3f512e81f45ab6084b608bc05fba5679b1d20d493aad98d422bdf593182604 diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md index d741fa433f33..6e649ee2fd36 100644 --- a/doc/lightning-autocleaninvoice.7.md +++ b/doc/lightning-autocleaninvoice.7.md @@ -25,7 +25,14 @@ On startup of the daemon, no autoclean is set up. RETURN VALUE ------------ -On success, an empty object is returned. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **enabled** (boolean): whether invoice autocleaning is active + +If **enabled** is *true*: + - **expired_by** (u64): how long an invoice must be expired (seconds) before we delete it + - **cycle_seconds** (u64): how long an invoice must be expired (seconds) before we delete it +[comment]: # (GENERATE-FROM-SCHEMA-END) AUTHOR ------ @@ -42,3 +49,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:2accf7788133af97ae097f7e4e8a80b35bbb431eb7e787e5ae12dd5c7d2c296d) diff --git a/doc/schemas/autocleaninvoice.schema.json b/doc/schemas/autocleaninvoice.schema.json new file mode 100644 index 000000000000..7ed3db3d513b --- /dev/null +++ b/doc/schemas/autocleaninvoice.schema.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ "enabled" ], + "properties": { + "enabled": { + "type": "boolean", + "description": "whether invoice autocleaning is active" + } + }, + "if": { + "properties": { + "enabled": { + "type": "boolean", + "enum": [ true ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "expired_by", "cycle_seconds" ], + "properties": { + "enabled": { }, + "expired_by": { + "type": "u64", + "description": "how long an invoice must be expired (seconds) before we delete it" + }, + "cycle_seconds": { + "type": "u64", + "description": "how long an invoice must be expired (seconds) before we delete it" + } + } + }, + "else": { + "additionalProperties": false, + "properties": { + "enabled": { } + } + } +} From 5d7203ed6b18a30e55777bba72ee0ee7df965f3d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:22:01 +0930 Subject: [PATCH 14/62] doc/schemas: create close schema. Signed-off-by: Rusty Russell --- doc/lightning-close.7 | 24 ++++++++++++++------ doc/lightning-close.7.md | 15 ++++++++----- doc/schemas/close.schema.json | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 doc/schemas/close.schema.json diff --git a/doc/lightning-close.7 b/doc/lightning-close.7 index e6478dd0638b..c6313e41716a 100644 --- a/doc/lightning-close.7 +++ b/doc/lightning-close.7 @@ -83,13 +83,23 @@ if the peer is offline and we are waiting\. .SH RETURN VALUE -On success, an object with fields \fItx\fR and \fItxid\fR containing the closing -transaction are returned\. It will also have a field \fItype\fR which is -either the JSON string \fImutual\fR or the JSON string \fIunilateral\fR\. A -\fImutual\fR close means that we could negotiate a close with the peer, -while a \fIunilateral\fR close means that the \fIforce\fR flag was set and we -had to close the channel without waiting for the counterparty\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBtype\fR (string): Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel (one of "mutual", "unilateral", "unopened") + +.RE + +If \fBtype\fR is "mutual" or "unilateral": + +.RS +.IP \[bu] +\fBtx\fR (hex): the raw bitcoin transaction used to close the channel (if it was open) +.IP \[bu] +\fBtxid\fR (txid): the transaction id of the \fItx\fR field + +.RE A unilateral close may still occur at any time if the peer did not behave correctly during the close negotiation\. @@ -110,4 +120,4 @@ ZmnSCPxj \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:c8a4fe414eeb72880346601be2e73f1ad6decb31b4d4e291549d8b339e47b68d +\" SHA256STAMP:507a9ca707e244eef65c5e16daa5a4d7ba8f59e93e988d252f7e854ae9f44781 diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 676272a48824..306598ca7a85 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -74,12 +74,14 @@ if the peer is offline and we are waiting. RETURN VALUE ------------ -On success, an object with fields *tx* and *txid* containing the closing -transaction are returned. It will also have a field *type* which is -either the JSON string *mutual* or the JSON string *unilateral*. A -*mutual* close means that we could negotiate a close with the peer, -while a *unilateral* close means that the *force* flag was set and we -had to close the channel without waiting for the counterparty. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **type** (string): Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel (one of "mutual", "unilateral", "unopened") + +If **type** is "mutual" or "unilateral": + - **tx** (hex): the raw bitcoin transaction used to close the channel (if it was open) + - **txid** (txid): the transaction id of the *tx* field +[comment]: # (GENERATE-FROM-SCHEMA-END) A unilateral close may still occur at any time if the peer did not behave correctly during the close negotiation. @@ -102,3 +104,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:9159304cd705d8135c32e12bd029c0e95baff0d495e6f9092a75888dab2f5fb3) diff --git a/doc/schemas/close.schema.json b/doc/schemas/close.schema.json new file mode 100644 index 000000000000..b28dee6459c9 --- /dev/null +++ b/doc/schemas/close.schema.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ "type" ], + "properties": { + "type": { + "type": "string", + "enum": [ "mutual", "unilateral", "unopened" ], + "description": "Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel" + } + }, + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "mutual", "unilateral" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "tx", "txid" ], + "properties": { + "type": { }, + "tx": { + "type": "hex", + "description": "the raw bitcoin transaction used to close the channel (if it was open)" + }, + "txid": { + "type": "txid", + "description": "the transaction id of the *tx* field" + } + } + }, + "else": { + "additionalProperties": false, + "properties": { + "type": { } + } + } +} From 4b46190cc47ae99d4a0546487b7efc2d3e3f56cb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:23:01 +0930 Subject: [PATCH 15/62] doc/schemas: schema for connect. Signed-off-by: Rusty Russell --- doc/lightning-connect.7 | 45 +++++++++++++++--- doc/lightning-connect.7.md | 21 +++++++-- doc/schemas/connect.schema.json | 82 +++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 doc/schemas/connect.schema.json diff --git a/doc/lightning-connect.7 b/doc/lightning-connect.7 index 5ee4cb737761..4bafbaf8a3ee 100644 --- a/doc/lightning-connect.7 +++ b/doc/lightning-connect.7 @@ -42,12 +42,45 @@ another node\. Once the peer is connected a channel can be opened with .SH RETURN VALUE -On success the peer \fIid\fR is returned, as well as a hexidecimal -\fIfeatures\fR bitmap, a \fIdirection\fR ("in" if they connected to us, "out" -if we connected to them") and an \fIaddress\fR object as per -\fBlightning-listnodes\fR(7)\. Note that \fIaddress\fR will be less useful if -"direction" is "in", especially if a proxy is in use\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBid\fR (pubkey): the peer we connected to +.IP \[bu] +\fBfeatures\fR (hex): BOLT 9 features bitmap offered by peer +.IP \[bu] +\fBdirection\fR (string): Whether they initiated connection or we did (one of "in", "out") +.IP \[bu] + +\fBaddress\fR (object): Address information (mainly useful if \fBdirection\fR is \fIout\fR): + +.RS +.IP \[bu] +\fBtype\fR (string): Type of connection (\fItorv2\fR/\fItorv3\fR only if \fBdirection\fR is \fIout\fR) (one of "local socket", "ipv4", "ipv6", "torv2", "torv3") + +.RE + +If \fBtype\fR is "local socket": + +.RS +.IP \[bu] +\fBsocket\fR (string): socket filename + +.RE + +If \fBtype\fR is "ipv4", "ipv6", "torv2" or "torv3": + +.RS +.IP \[bu] +\fBaddress\fR (string): address in expected format for \fBtype\fR +.IP \[bu] +\fBport\fR (u16): port number + +.RE + + +.RE .SH ERRORS On failure, one of the following errors will be returned: @@ -97,4 +130,4 @@ Felix \fI is the original author of this manpage\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:a392b6683fad5fe218e7a985e1eaf5d7439f38d7c74212c275cfa64db284efc0 +\" SHA256STAMP:ff422184feb295e6d3e17e88c0305405edcb24eac59482a43caf750ef281e0ed diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index 823cadda7b2d..be1df3d74914 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -39,11 +39,21 @@ lightning-fundchannel(7). RETURN VALUE ------------ -On success the peer *id* is returned, as well as a hexidecimal -*features* bitmap, a *direction* ("in" if they connected to us, "out" -if we connected to them") and an *address* object as per -lightning-listnodes(7). Note that *address* will be less useful if -"direction" is "in", especially if a proxy is in use. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **id** (pubkey): the peer we connected to +- **features** (hex): BOLT 9 features bitmap offered by peer +- **direction** (string): Whether they initiated connection or we did (one of "in", "out") +- **address** (object): Address information (mainly useful if **direction** is *out*): + - **type** (string): Type of connection (*torv2*/*torv3* only if **direction** is *out*) (one of "local socket", "ipv4", "ipv6", "torv2", "torv3") + + If **type** is "local socket": + - **socket** (string): socket filename + + If **type** is "ipv4", "ipv6", "torv2" or "torv3": + - **address** (string): address in expected format for **type** + - **port** (u16): port number +[comment]: # (GENERATE-FROM-SCHEMA-END) ERRORS ------ @@ -78,3 +88,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:5b168e7998d3db6a842eabf92bcbb74352fe831726ea42a801e39ff5c3f812ca) diff --git a/doc/schemas/connect.schema.json b/doc/schemas/connect.schema.json new file mode 100644 index 000000000000..3ce55591c5fb --- /dev/null +++ b/doc/schemas/connect.schema.json @@ -0,0 +1,82 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "id", "features", "direction", "address" ], + "properties": { + "id": { + "type": "pubkey", + "description": "the peer we connected to" + }, + "features": { + "type": "hex", + "description": "BOLT 9 features bitmap offered by peer" + }, + "direction": { + "type": "string", + "enum": [ "in", "out" ], + "description": "Whether they initiated connection or we did" + }, + "address": { + "type": "object", + "description": "Address information (mainly useful if **direction** is *out*)", + "additionalProperties": true, + "required": [ "type" ], + "properties": { + "type": { + "type": "string", + "enum": [ "local socket", "ipv4", "ipv6", "torv2", "torv3" ], + "description": "Type of connection (*torv2*/*torv3* only if **direction** is *out*)" + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "local socket" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "socket" ], + "properties": { + "type": { }, + "socket": { + "type": "string", + "description": "socket filename" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "ipv4", "ipv6", "torv2", "torv3" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "address", "port" ], + "properties": { + "type": { }, + "address": { + "type": "string", + "description": "address in expected format for **type**" + }, + "port": { + "type": "u16", + "description": "port number" + } + } + } + } + ] + } + } +} From d1b42cccd1010a8a23c372666c469962eb107ce8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:24:01 +0930 Subject: [PATCH 16/62] doc/schemas: decode, decodepay One of the more complex ones. Signed-off-by: Rusty Russell --- doc/lightning-decode.7 | 443 ++++++++++++---- doc/lightning-decode.7.md | 237 +++++---- doc/lightning-decodepay.7 | 77 ++- doc/lightning-decodepay.7.md | 52 +- doc/schemas/decode.schema.json | 806 ++++++++++++++++++++++++++++++ doc/schemas/decodepay.schema.json | 146 ++++++ 6 files changed, 1524 insertions(+), 237 deletions(-) create mode 100644 doc/schemas/decode.schema.json create mode 100644 doc/schemas/decodepay.schema.json diff --git a/doc/lightning-decode.7 b/doc/lightning-decode.7 index 0b4e80e4d857..a497de756f0b 100644 --- a/doc/lightning-decode.7 +++ b/doc/lightning-decode.7 @@ -17,140 +17,385 @@ other formats in future\. .SH RETURN VALUE -On success, an object is returned with a \fItype\fR member indicating the -type of the decoding: +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBtype\fR (string): what kind of object it decoded to (one of "bolt12 offer", "bolt12 invoice", "bolt12 invoice_request", "bolt11 invoice") +.IP \[bu] +\fBvalid\fR (boolean): if this is false, you \fIMUST\fR not use the result except for diagnostics! + +.RE -\fItype\fR: "bolt12 offer" +If \fBtype\fR is "bolt12 offer", and \fBvalid\fR is \fItrue\fR: -.nf .RS -- *offer_id*: the id of this offer (merkle hash of non-signature fields) -- *chains* (optional): if set, an array of genesis hashes of supported chains. (Unset implies bitcoin mainnet). -- *currency* (optional): ISO 4217 code of the currency. -- *minor_unit* (optional): the number of decimal places to apply to amount (if currency known) -- *amount* (optional): the amount in the *currency* adjusted by *minor_unit*, if any. -- *amount_msat* (optional): the amount (with "msat" appended) if there is no *currency*. -- *send_invoice* (optional): `true` if this is a send_invoice offer. -- *refund_for* (optional): the sha256 payment_preimage of invoice this is a refund for. -- *description* (optional): the UTF-8 description of the purpose of the offer. -- *vendor* (optional): the UTF-8 name of the vendor for this offer. -- *features* (optional): hex array of feature bits. -- *absolute_expiry* (optional): UNIX timestamp of when this offer expires. -- *paths* (optional): Array of objects containing *blinding*, *path* array; each *path* entry contains an object with *node_id* and *enctlv*. -- *quantity_min* (optional): minimum valid quantity for offer responses -- *quantity_max* (optional): maximum valid quantity for offer responses -- *recurrence* (optional): an object containing *time_unit*, *time_unit_name* (optional, a string), *period*, *basetime* (optional), *start_any_period* (optional), *limit* (optional), and *paywindow* (optional) object containing *seconds_before*, *seconds_after* and *proportional_amount* (optional). -- *node_id*: 32-byte (x-only) public key of the offering node. -- *signature*: BIP-340 signature of the *node_id* on this offer. +.IP \[bu] +\fBoffer_id\fR (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) +.IP \[bu] +\fBnode_id\fR (pubkey32): x-only public key of the offering node +.IP \[bu] +\fBsignature\fR (bip340sig): BIP-340 signature of the \fInode_id\fR on this offer +.IP \[bu] +\fBdescription\fR (string): the description of the purpose of the offer +.IP \[bu] +\fBchains\fR (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only): +.RS +.IP \[bu] +the genesis blockhash (always 64 characters) +.RE + +.IP \[bu] +\fBcurrency\fR (string, optional): ISO 4217 code of the currency (missing implies Bitcoin) (always 3 characters) +.IP \[bu] +\fBminor_unit\fR (u32, optional): the number of decimal places to apply to amount (if currency known) +.IP \[bu] +\fBamount\fR (u64, optional): the amount in the \fIcurrency\fR adjusted by \fIminor_unit\fR, if any +.IP \[bu] +\fBamount_msat\fR (msat, optional): the amount in bitcoin (if specified, and no \fIcurrency\fR) +.IP \[bu] +\fBsend_invoice\fR (boolean, optional): present if this is a send_invoice offer (always \fItrue\fR) +.IP \[bu] +\fBrefund_for\fR (hex, optional): the \fIpayment_preimage\fR of invoice this is a refund for (always 64 characters) +.IP \[bu] +\fBvendor\fR (string, optional): the name of the vendor for this offer +.IP \[bu] +\fBfeatures\fR (hex, optional): the array of feature bits for this offer +.IP \[bu] +\fBabsolute_expiry\fR (u64, optional): UNIX timestamp of when this offer expires +.IP \[bu] +\fBpaths\fR (array of objects, optional): Paths to the destination: +.RS +.IP \[bu] +\fBblinding\fR (pubkey): blinding factor for this path +.IP \[bu] +\fBpath\fR (array of objects): an individual path: +.RS +.IP \[bu] +\fBnode_id\fR (pubkey): node_id of the hop +.IP \[bu] +\fBenctlv\fR (hex): encrypted TLV entry for this hop .RE -.fi -\fItype\fR: "bolt12 invoice" +.RE -.nf +.IP \[bu] +\fBquantity_min\fR (u64, optional): the minimum quantity +.IP \[bu] +\fBquantity_max\fR (u64, optional): the maximum quantity +.IP \[bu] +\fBrecurrence\fR (object, optional): how often to this offer should be used: .RS -- *chains* (optional): if set, an array of genesis hashes of supported chains. (Unset implies bitcoin mainnet). -- *offer_id* (optional): id of the offer this invoice is for. -- *amount_msat* (optional): the amount (with "msat" appended). -- *description* (optional): the UTF-8 description of the purpose of the offer. -- *vendor* (optional): the UTF-8 name of the vendor for this offer. -- *features* (optional): hex array of feature bits. -- *paths* (optional): Array of objects containing *blinding*, *path* array; each *path* entry contains an object with *node_id*, *enctlv*, *fee_base_msat* (optional), *fee_proportional_millionths* (optional), *cltv_expiry_delta* (optional), and *features* (optional). -- *quantity* (optional): quantity of items. -- *send_invoice* (optional): `true` if this is a response to a send_invoice offer. -- *refund_for* (optional): the sha256 payment_preimage of invoice this is a refund for. -- *recurrence_counter* (optional): the zero-based number of the invoice for a recurring offer. -- *recurrence_start* (optional): the zero-based offet of the first invoice for the recurring offer. -- *recurrence_basetime* (optional): the UNIX timestamp of the first period of the offer. -- *payer_key* (optional): the 32-byte (x-only) id of the payer. -- *payer_info* (optional): a variable-length blob for the payer to derive their key. -- *timestamp* (optional): the UNIX timestamp of the invoice. -- *payment_hash* (optional): the hex SHA256 of the payment_preimage. -- *expiry* (optional): seconds from *timestamp* when invoice expires. -- *min_final_cltv_expiry*: required CLTV for final hop. -- *fallbacks* (optional): an array containing objects with *version*, and *hex* fields for each fallback address, and *address* (optional) if it's parsable. -- *refund_signature* (optional): BIP-340 signature of the *payer_key* on this offer. -- *node_id*: 32-byte (x-only) public key of the invoicing node. -- *signature*: BIP-340 signature of the *node_id* on this invoice. +.IP \[bu] +\fBtime_unit\fR (u32): the BOLT12 time unit +.IP \[bu] +\fBperiod\fR (u32): how many \fItime_unit\fR per payment period +.IP \[bu] +\fBtime_unit_name\fR (string, optional): the name of \fItime_unit\fR (if valid) +.IP \[bu] +\fBbasetime\fR (u64, optional): period starts at this UNIX timestamp +.IP \[bu] +\fBstart_any_period\fR (u64, optional): you can start at any period (only if \fBbasetime\fR present) +.IP \[bu] +\fBlimit\fR (u32, optional): maximum period number for recurrence +.IP \[bu] +\fBpaywindow\fR (object, optional): when within a period will payment be accepted (default is prior and during the period): +.RS +.IP \[bu] +\fBseconds_before\fR (u32): seconds prior to period start +.IP \[bu] +\fBseconds_after\fR (u32): seconds after to period start +.IP \[bu] +\fBproportional_amount\fR (boolean, optional): amount should be scaled if payed after period start (always \fItrue\fR) +.RE + + +.RE + +.IP \[bu] +the following warnings are possible: +.RS +.IP \[bu] +\fBwarning_offer_unknown_currency\fR: The currency code is unknown (so no \fBminor_unit\fR) .RE -.fi -\fItype\fR: "bolt12 invoice_request" +.RE + +If \fBtype\fR is "bolt12 offer", and \fBvalid\fR is \fIfalse\fR: -.nf .RS -- *chains* (optional): if set, an array of genesis hashes of supported chains. (Unset implies bitcoin mainnet). -- *offer_id* (optional): id of the offer this invoice is for. -- *amount_msat* (optional): the amount (with "msat" appended). -- *features* (optional): hex array of feature bits. -- *quantity* (optional): quantity of items. -- *recurrence_counter* (optional): the zero-based number of the invoice for a recurring offer. -- *recurrence_start* (optional): the zero-based offet of the first invoice for the recurring offer. -- *payer_key* (optional): the 32-byte (x-only) id of the payer. -- *payer_info* (optional): a variable-length blob for the payer to derive their key. -- *recurrence_signature* (optional): BIP-340 signature of the *payer_key* on this offer. +.IP \[bu] +the following warnings are possible: +.RS +.IP \[bu] +\fBwarning_offer_missing_description\fR: No \fBdescription\fR + +.RE .RE -.fi +If \fBtype\fR is "bolt12 invoice", and \fBvalid\fR is \fItrue\fR: -\fItype\fR: "bolt11 invoice" +.RS +.IP \[bu] +\fBnode_id\fR (pubkey32): x-only public key of the offering node +.IP \[bu] +\fBsignature\fR (bip340sig): BIP-340 signature of the \fInode_id\fR on this offer +.IP \[bu] +\fBamount_msat\fR (msat): the amount in bitcoin +.IP \[bu] +\fBdescription\fR (string): the description of the purpose of the offer +.IP \[bu] +\fBtimestamp\fR (u64): the UNIX timestamp of the invoice +.IP \[bu] +\fBpayment_hash\fR (hex): the hash of the \fIpayment_preimage\fR (always 64 characters) +.IP \[bu] +\fBrelative_expiry\fR (u32): the number of seconds after \fItimestamp\fR when this expires +.IP \[bu] +\fBmin_final_cltv_expiry\fR (u32): the number of blocks required by destination +.IP \[bu] +\fBoffer_id\fR (hex, optional): the id of this offer (merkle hash of non-signature fields) (always 64 characters) +.IP \[bu] +\fBchains\fR (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only): +.RS +.IP \[bu] +the genesis blockhash (always 64 characters) + +.RE -.nf +.IP \[bu] +\fBsend_invoice\fR (boolean, optional): present if this offer was a send_invoice offer (always \fItrue\fR) +.IP \[bu] +\fBrefund_for\fR (hex, optional): the \fIpayment_preimage\fR of invoice this is a refund for (always 64 characters) +.IP \[bu] +\fBvendor\fR (string, optional): the name of the vendor for this offer +.IP \[bu] +\fBfeatures\fR (hex, optional): the array of feature bits for this offer +.IP \[bu] +\fBpaths\fR (array of objects, optional): Paths to the destination: +.RS +.IP \[bu] +\fBblinding\fR (pubkey): blinding factor for this path +.IP \[bu] +\fBpath\fR (array of objects): an individual path: .RS -- *currency*: the BIP173 name for the currency. -- *timestamp*: the UNIX-style timestamp of the invoice. -- *expiry*: the number of seconds this is valid after *timestamp*. -- *payee*: the public key of the recipient. -- *payment_hash*: the payment hash of the request. -- *signature*: the DER-encoded signature. -- *description*: the UTF-8 description of the purpose of the purchase. -- *msatoshi* (optional): the number of millisatoshi requested (if any). -- *amount_msat* (optional): the same as above, with *msat* appended (if any). -- *fallbacks* (optional): array of fallback address object containing a *hex* string, and both *type* and *addr* if it is recognized as one of *P2PKH*, *P2SH*, *P2WPKH*, or *P2WSH*. -- *routes* (optional): an array of routes. Each route is an arrays of objects, each containing *pubkey*, *short_channel_id*, *fee_base_msat*, *fee_proportional_millionths* and *cltv_expiry_delta*. -- *extra* (optional): an array of objects representing unknown fields, each with one-character *tag* and a *data* bech32 string. +.IP \[bu] +\fBnode_id\fR (pubkey): node_id of the hop +.IP \[bu] +\fBenctlv\fR (hex): encrypted TLV entry for this hop + +.RE .RE -.fi +.IP \[bu] +\fBquantity\fR (u64, optional): the quantity ordered +.IP \[bu] +\fBrecurrence_counter\fR (u32, optional): the 0-based counter for a recurring payment +.IP \[bu] +\fBrecurrence_start\fR (u32, optional): the optional start period for a recurring payment +.IP \[bu] +\fBrecurrence_basetime\fR (u32, optional): the UNIX timestamp of the first recurrence period start +.IP \[bu] +\fBpayer_key\fR (pubkey32, optional): the transient key which identifies the payer +.IP \[bu] +\fBpayer_info\fR (hex, optional): the payer-provided blob to derive payer_key +.IP \[bu] +\fBfallbacks\fR (array of objects, optional): onchain addresses: +.RS +.IP \[bu] +\fBversion\fR (u8): Segwit address version +.IP \[bu] +\fBhex\fR (hex): Raw encoded segwit address +.IP \[bu] +\fBaddress\fR (string, optional): bech32 segwit address -Some invalid strings can still be parsed, and warnings will be given: +.RE -.nf +.IP \[bu] +\fBrefund_signature\fR (bip340sig, optional): the payer key signature to get a refund + +.RE + +If \fBtype\fR is "bolt12 invoice", and \fBvalid\fR is \fIfalse\fR: + +.RS +.IP \[bu] +\fBfallbacks\fR (array of objects, optional): .RS -- "warning_offer_unknown_currency": unknown or invalid *currency* code. -- "warning_offer_missing_description": invalid due to missing description. -- "warning_invoice_invalid_blinded_payinfo": blinded_payinfo does not match paths. -- "warning_invoice_fallbacks_version_invalid": a fallback version is not a valid segwit version -- "warning_invoice_fallbacks_address_invalid": a fallback address is not a valid segwit address (within an object in the *fallback* array) -- "warning_invoice_missing_amount": amount field is missing. -- "warning_invoice_missing_description": description field is missing. -- "warning_invoice_missing_blinded_payinfo": blindedpay is missing. -- "warning_invoice_missing_recurrence_basetime: recurrence_basetime is missing. -- "warning_invoice_missing_timestamp": timestamp is missing. -- "warning_invoice_missing_payment_hash": payment hash is missing. -- "warning_invoice_refund_signature_missing_payer_key": payer_key is missing for refund_signature. -- "warning_invoice_refund_signature_invalid": refund_signature does not match. -- "warning_invoice_refund_missing_signature": refund_signature is missing. -- "warning_invoice_request_missing_offer_id": offer_id is missing. -- "warning_invoice_request_missing_payer_key": payer_key is missing. -- "warning_invoice_request_invalid_recurrence_signature": recurrence_signature does not match. -- "warning_invoice_request_missing_recurrence_signature": recurrence_signature is missing. +.IP \[bu] +the following warnings are possible: +.RS +.IP \[bu] +\fBwarning_invoice_fallbacks_version_invalid\fR: \fBversion\fR is > 16 + +.RE + +.RE + +.IP \[bu] +the following warnings are possible: +.RS +.IP \[bu] +\fBwarning_invoice_missing_amount\fR: *\fIamount_msat\fR missing +.IP \[bu] +\fBwarning_invoice_missing_description\fR: No \fBdescription\fR +.IP \[bu] +\fBwarning_invoice_missing_blinded_payinfo\fR: Has \fBpaths\fR without payinfo +.IP \[bu] +\fBwarning_invoice_invalid_blinded_payinfo\fR: Does not have exactly one payinfo for each of \fBpaths\fR +.IP \[bu] +\fBwarning_invoice_missing_recurrence_basetime\fR: Has \fBrecurrence_counter\fR without \fBrecurrence_basetime\fR +.IP \[bu] +\fBwarning_invoice_missing_timestamp\fR: Missing \fBtimestamp\fR +.IP \[bu] +\fBwarning_invoice_missing_payment_hash\fR: Missing \fBpayment_hash\fR +.IP \[bu] +\fBwarning_invoice_refund_signature_missing_payer_key\fR: Missing \fBpayer_key\fR for refund_signature +.IP \[bu] +\fBwarning_invoice_refund_signature_invalid\fR: \fBrefund_signature\fR incorrect +.IP \[bu] +\fBwarning_invoice_refund_missing_signature\fR: No \fBrefund_signature\fR .RE -.fi + +.RE + +If \fBtype\fR is "bolt12 invoice_request", and \fBvalid\fR is \fItrue\fR: + +.RS +.IP \[bu] +\fBoffer_id\fR (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) +.IP \[bu] +\fBpayer_key\fR (pubkey32): the transient key which identifies the payer +.IP \[bu] +\fBchains\fR (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only): +.RS +.IP \[bu] +the genesis blockhash (always 64 characters) + +.RE + +.IP \[bu] +\fBamount_msat\fR (msat, optional): the amount in bitcoin +.IP \[bu] +\fBfeatures\fR (hex, optional): the array of feature bits for this offer +.IP \[bu] +\fBquantity\fR (u64, optional): the quantity ordered +.IP \[bu] +\fBrecurrence_counter\fR (u32, optional): the 0-based counter for a recurring payment +.IP \[bu] +\fBrecurrence_start\fR (u32, optional): the optional start period for a recurring payment +.IP \[bu] +\fBpayer_info\fR (hex, optional): the payer-provided blob to derive payer_key +.IP \[bu] +\fBrecurrence_signature\fR (bip340sig, optional): the payer key signature + +.RE + +If \fBtype\fR is "bolt12 invoice_request", and \fBvalid\fR is \fIfalse\fR: + +.RS +.IP \[bu] +the following warnings are possible: +.RS +.IP \[bu] +\fBwarning_invoice_request_missing_offer_id\fR: No \fBoffer_id\fR +.IP \[bu] +\fBwarning_invoice_request_missing_payer_key\fR: No \fBpayer_key\fR +.IP \[bu] +\fBwarning_invoice_request_missing_recurrence_signature\fR: No \fBrecurrence_signature\fR +.IP \[bu] +\fBwarning_invoice_request_invalid_recurrence_signature\fR: \fBrecurrence_signature\fR incorrect + +.RE + + +.RE + +If \fBtype\fR is "bolt11 invoice", and \fBvalid\fR is \fItrue\fR: + +.RS +.IP \[bu] +\fBcurrency\fR (string): the BIP173 name for the currency +.IP \[bu] +\fBcreated_at\fR (u64): the UNIX-style timestamp of the invoice +.IP \[bu] +\fBexpiry\fR (u64): the number of seconds this is valid after \fItimestamp\fR +.IP \[bu] +\fBpayee\fR (pubkey): the public key of the recipient +.IP \[bu] +\fBpayment_hash\fR (hex): the hash of the \fIpayment_preimage\fR (always 64 characters) +.IP \[bu] +\fBsignature\fR (signature): signature of the \fIpayee\fR on this invoice +.IP \[bu] +\fBmin_final_cltv_expiry\fR (u32): the minimum CLTV delay for the final node +.IP \[bu] +\fBamount_msat\fR (msat, optional): Amount the invoice asked for +.IP \[bu] +\fBdescription\fR (string, optional): the description of the purpose of the purchase +.IP \[bu] +\fBdescription_hash\fR (hex, optional): the hash of the description, in place of \fIdescription\fR (always 64 characters) +.IP \[bu] +\fBpayment_secret\fR (hex, optional): the secret to hand to the payee node (always 64 characters) +.IP \[bu] +\fBfeatures\fR (hex, optional): the features bitmap for this invoice +.IP \[bu] +\fBfallbacks\fR (array of objects, optional): onchain addresses: +.RS +.IP \[bu] +\fBtype\fR (string): the address type (if known) (one of "P2PKH", "P2SH", "P2WPKH", "P2WSH") +.IP \[bu] +\fBhex\fR (hex): Raw encoded address +.IP \[bu] +\fBaddr\fR (string, optional): the address in appropriate format for \fItype\fR + +.RE + +.IP \[bu] +\fBroutes\fR (array of arrays, optional): Route hints to the \fIpayee\fR: +.RS +.IP \[bu] +hops in the route: +.RS +.IP \[bu] +\fBpubkey\fR (pubkey): the public key of the node +.IP \[bu] +\fBshort_channel_id\fR (short_channel_id): a channel to the next peer +.IP \[bu] +\fBfee_base_msat\fR (u32): the base fee for payments +.IP \[bu] +\fBfee_proportional_millionths\fR (u32): the parts-per-million fee for payments +.IP \[bu] +\fBcltv_expiry_delta\fR (u32): the CLTV delta across this hop + +.RE + + +.RE + +.IP \[bu] +\fBextra\fR (array of objects, optional): Any extra fields we didn't know how to parse: +.RS +.IP \[bu] +\fBtag\fR (string): The bech32 letter which identifies this field (always 1 characters) +.IP \[bu] +\fBdata\fR (string): The bech32 data for this field + +.RE + + +.RE .SH AUTHOR Rusty Russell \fI is mainly responsible\. @@ -169,4 +414,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:4a01fe92de9520e78656495ff9093184cdf4836283af7102ee6b50ab0c826132 +\" SHA256STAMP:403b7cf3cadd8b260b8b2b02746c76d7d21a8319fe386b4174f4f58b3e5dcdbd diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 6966b9ecc2f9..356162493914 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -19,99 +19,149 @@ other formats in future. RETURN VALUE ------------ -On success, an object is returned with a *type* member indicating the -type of the decoding: - -*type*: "bolt12 offer" - - *offer_id*: the id of this offer (merkle hash of non-signature fields) - - *chains* (optional): if set, an array of genesis hashes of supported chains. (Unset implies bitcoin mainnet). - - *currency* (optional): ISO 4217 code of the currency. - - *minor_unit* (optional): the number of decimal places to apply to amount (if currency known) - - *amount* (optional): the amount in the *currency* adjusted by *minor_unit*, if any. - - *amount_msat* (optional): the amount (with "msat" appended) if there is no *currency*. - - *send_invoice* (optional): `true` if this is a send_invoice offer. - - *refund_for* (optional): the sha256 payment_preimage of invoice this is a refund for. - - *description* (optional): the UTF-8 description of the purpose of the offer. - - *vendor* (optional): the UTF-8 name of the vendor for this offer. - - *features* (optional): hex array of feature bits. - - *absolute_expiry* (optional): UNIX timestamp of when this offer expires. - - *paths* (optional): Array of objects containing *blinding*, *path* array; each *path* entry contains an object with *node_id* and *enctlv*. - - *quantity_min* (optional): minimum valid quantity for offer responses - - *quantity_max* (optional): maximum valid quantity for offer responses - - *recurrence* (optional): an object containing *time_unit*, *time_unit_name* (optional, a string), *period*, *basetime* (optional), *start_any_period* (optional), *limit* (optional), and *paywindow* (optional) object containing *seconds_before*, *seconds_after* and *proportional_amount* (optional). - - *node_id*: 32-byte (x-only) public key of the offering node. - - *signature*: BIP-340 signature of the *node_id* on this offer. - -*type*: "bolt12 invoice" - - *chains* (optional): if set, an array of genesis hashes of supported chains. (Unset implies bitcoin mainnet). - - *offer_id* (optional): id of the offer this invoice is for. - - *amount_msat* (optional): the amount (with "msat" appended). - - *description* (optional): the UTF-8 description of the purpose of the offer. - - *vendor* (optional): the UTF-8 name of the vendor for this offer. - - *features* (optional): hex array of feature bits. - - *paths* (optional): Array of objects containing *blinding*, *path* array; each *path* entry contains an object with *node_id*, *enctlv*, *fee_base_msat* (optional), *fee_proportional_millionths* (optional), *cltv_expiry_delta* (optional), and *features* (optional). - - *quantity* (optional): quantity of items. - - *send_invoice* (optional): `true` if this is a response to a send_invoice offer. - - *refund_for* (optional): the sha256 payment_preimage of invoice this is a refund for. - - *recurrence_counter* (optional): the zero-based number of the invoice for a recurring offer. - - *recurrence_start* (optional): the zero-based offet of the first invoice for the recurring offer. - - *recurrence_basetime* (optional): the UNIX timestamp of the first period of the offer. - - *payer_key* (optional): the 32-byte (x-only) id of the payer. - - *payer_info* (optional): a variable-length blob for the payer to derive their key. - - *timestamp* (optional): the UNIX timestamp of the invoice. - - *payment_hash* (optional): the hex SHA256 of the payment_preimage. - - *expiry* (optional): seconds from *timestamp* when invoice expires. - - *min_final_cltv_expiry*: required CLTV for final hop. - - *fallbacks* (optional): an array containing objects with *version*, and *hex* fields for each fallback address, and *address* (optional) if it's parsable. - - *refund_signature* (optional): BIP-340 signature of the *payer_key* on this offer. - - *node_id*: 32-byte (x-only) public key of the invoicing node. - - *signature*: BIP-340 signature of the *node_id* on this invoice. - -*type*: "bolt12 invoice_request" - - *chains* (optional): if set, an array of genesis hashes of supported chains. (Unset implies bitcoin mainnet). - - *offer_id* (optional): id of the offer this invoice is for. - - *amount_msat* (optional): the amount (with "msat" appended). - - *features* (optional): hex array of feature bits. - - *quantity* (optional): quantity of items. - - *recurrence_counter* (optional): the zero-based number of the invoice for a recurring offer. - - *recurrence_start* (optional): the zero-based offet of the first invoice for the recurring offer. - - *payer_key* (optional): the 32-byte (x-only) id of the payer. - - *payer_info* (optional): a variable-length blob for the payer to derive their key. - - *recurrence_signature* (optional): BIP-340 signature of the *payer_key* on this offer. - -*type*: "bolt11 invoice" - - *currency*: the BIP173 name for the currency. - - *timestamp*: the UNIX-style timestamp of the invoice. - - *expiry*: the number of seconds this is valid after *timestamp*. - - *payee*: the public key of the recipient. - - *payment_hash*: the payment hash of the request. - - *signature*: the DER-encoded signature. - - *description*: the UTF-8 description of the purpose of the purchase. - - *msatoshi* (optional): the number of millisatoshi requested (if any). - - *amount_msat* (optional): the same as above, with *msat* appended (if any). - - *fallbacks* (optional): array of fallback address object containing a *hex* string, and both *type* and *addr* if it is recognized as one of *P2PKH*, *P2SH*, *P2WPKH*, or *P2WSH*. - - *routes* (optional): an array of routes. Each route is an arrays of objects, each containing *pubkey*, *short_channel_id*, *fee_base_msat*, *fee_proportional_millionths* and *cltv_expiry_delta*. - - *extra* (optional): an array of objects representing unknown fields, each with one-character *tag* and a *data* bech32 string. - -Some invalid strings can still be parsed, and warnings will be given: - - "warning_offer_unknown_currency": unknown or invalid *currency* code. - - "warning_offer_missing_description": invalid due to missing description. - - "warning_invoice_invalid_blinded_payinfo": blinded_payinfo does not match paths. - - "warning_invoice_fallbacks_version_invalid": a fallback version is not a valid segwit version - - "warning_invoice_fallbacks_address_invalid": a fallback address is not a valid segwit address (within an object in the *fallback* array) - - "warning_invoice_missing_amount": amount field is missing. - - "warning_invoice_missing_description": description field is missing. - - "warning_invoice_missing_blinded_payinfo": blindedpay is missing. - - "warning_invoice_missing_recurrence_basetime: recurrence_basetime is missing. - - "warning_invoice_missing_timestamp": timestamp is missing. - - "warning_invoice_missing_payment_hash": payment hash is missing. - - "warning_invoice_refund_signature_missing_payer_key": payer_key is missing for refund_signature. - - "warning_invoice_refund_signature_invalid": refund_signature does not match. - - "warning_invoice_refund_missing_signature": refund_signature is missing. - - "warning_invoice_request_missing_offer_id": offer_id is missing. - - "warning_invoice_request_missing_payer_key": payer_key is missing. - - "warning_invoice_request_invalid_recurrence_signature": recurrence_signature does not match. - - "warning_invoice_request_missing_recurrence_signature": recurrence_signature is missing. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **type** (string): what kind of object it decoded to (one of "bolt12 offer", "bolt12 invoice", "bolt12 invoice_request", "bolt11 invoice") +- **valid** (boolean): if this is false, you *MUST* not use the result except for diagnostics! + +If **type** is "bolt12 offer", and **valid** is *true*: + - **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) + - **node_id** (pubkey32): x-only public key of the offering node + - **signature** (bip340sig): BIP-340 signature of the *node_id* on this offer + - **description** (string): the description of the purpose of the offer + - **chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only): + - the genesis blockhash (always 64 characters) + - **currency** (string, optional): ISO 4217 code of the currency (missing implies Bitcoin) (always 3 characters) + - **minor_unit** (u32, optional): the number of decimal places to apply to amount (if currency known) + - **amount** (u64, optional): the amount in the *currency* adjusted by *minor_unit*, if any + - **amount_msat** (msat, optional): the amount in bitcoin (if specified, and no *currency*) + - **send_invoice** (boolean, optional): present if this is a send_invoice offer (always *true*) + - **refund_for** (hex, optional): the *payment_preimage* of invoice this is a refund for (always 64 characters) + - **vendor** (string, optional): the name of the vendor for this offer + - **features** (hex, optional): the array of feature bits for this offer + - **absolute_expiry** (u64, optional): UNIX timestamp of when this offer expires + - **paths** (array of objects, optional): Paths to the destination: + - **blinding** (pubkey): blinding factor for this path + - **path** (array of objects): an individual path: + - **node_id** (pubkey): node_id of the hop + - **enctlv** (hex): encrypted TLV entry for this hop + - **quantity_min** (u64, optional): the minimum quantity + - **quantity_max** (u64, optional): the maximum quantity + - **recurrence** (object, optional): how often to this offer should be used: + - **time_unit** (u32): the BOLT12 time unit + - **period** (u32): how many *time_unit* per payment period + - **time_unit_name** (string, optional): the name of *time_unit* (if valid) + - **basetime** (u64, optional): period starts at this UNIX timestamp + - **start_any_period** (u64, optional): you can start at any period (only if **basetime** present) + - **limit** (u32, optional): maximum period number for recurrence + - **paywindow** (object, optional): when within a period will payment be accepted (default is prior and during the period): + - **seconds_before** (u32): seconds prior to period start + - **seconds_after** (u32): seconds after to period start + - **proportional_amount** (boolean, optional): amount should be scaled if payed after period start (always *true*) + - the following warnings are possible: + - **warning_offer_unknown_currency**: The currency code is unknown (so no **minor_unit**) + +If **type** is "bolt12 offer", and **valid** is *false*: + - the following warnings are possible: + - **warning_offer_missing_description**: No **description** + +If **type** is "bolt12 invoice", and **valid** is *true*: + - **node_id** (pubkey32): x-only public key of the offering node + - **signature** (bip340sig): BIP-340 signature of the *node_id* on this offer + - **amount_msat** (msat): the amount in bitcoin + - **description** (string): the description of the purpose of the offer + - **timestamp** (u64): the UNIX timestamp of the invoice + - **payment_hash** (hex): the hash of the *payment_preimage* (always 64 characters) + - **relative_expiry** (u32): the number of seconds after *timestamp* when this expires + - **min_final_cltv_expiry** (u32): the number of blocks required by destination + - **offer_id** (hex, optional): the id of this offer (merkle hash of non-signature fields) (always 64 characters) + - **chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only): + - the genesis blockhash (always 64 characters) + - **send_invoice** (boolean, optional): present if this offer was a send_invoice offer (always *true*) + - **refund_for** (hex, optional): the *payment_preimage* of invoice this is a refund for (always 64 characters) + - **vendor** (string, optional): the name of the vendor for this offer + - **features** (hex, optional): the array of feature bits for this offer + - **paths** (array of objects, optional): Paths to the destination: + - **blinding** (pubkey): blinding factor for this path + - **path** (array of objects): an individual path: + - **node_id** (pubkey): node_id of the hop + - **enctlv** (hex): encrypted TLV entry for this hop + - **quantity** (u64, optional): the quantity ordered + - **recurrence_counter** (u32, optional): the 0-based counter for a recurring payment + - **recurrence_start** (u32, optional): the optional start period for a recurring payment + - **recurrence_basetime** (u32, optional): the UNIX timestamp of the first recurrence period start + - **payer_key** (pubkey32, optional): the transient key which identifies the payer + - **payer_info** (hex, optional): the payer-provided blob to derive payer_key + - **fallbacks** (array of objects, optional): onchain addresses: + - **version** (u8): Segwit address version + - **hex** (hex): Raw encoded segwit address + - **address** (string, optional): bech32 segwit address + - **refund_signature** (bip340sig, optional): the payer key signature to get a refund + +If **type** is "bolt12 invoice", and **valid** is *false*: + - **fallbacks** (array of objects, optional): + - the following warnings are possible: + - **warning_invoice_fallbacks_version_invalid**: **version** is > 16 + - the following warnings are possible: + - **warning_invoice_missing_amount**: **amount_msat* missing + - **warning_invoice_missing_description**: No **description** + - **warning_invoice_missing_blinded_payinfo**: Has **paths** without payinfo + - **warning_invoice_invalid_blinded_payinfo**: Does not have exactly one payinfo for each of **paths** + - **warning_invoice_missing_recurrence_basetime**: Has **recurrence_counter** without **recurrence_basetime** + - **warning_invoice_missing_timestamp**: Missing **timestamp** + - **warning_invoice_missing_payment_hash**: Missing **payment_hash** + - **warning_invoice_refund_signature_missing_payer_key**: Missing **payer_key** for refund_signature + - **warning_invoice_refund_signature_invalid**: **refund_signature** incorrect + - **warning_invoice_refund_missing_signature**: No **refund_signature** + +If **type** is "bolt12 invoice_request", and **valid** is *true*: + - **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) + - **payer_key** (pubkey32): the transient key which identifies the payer + - **chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only): + - the genesis blockhash (always 64 characters) + - **amount_msat** (msat, optional): the amount in bitcoin + - **features** (hex, optional): the array of feature bits for this offer + - **quantity** (u64, optional): the quantity ordered + - **recurrence_counter** (u32, optional): the 0-based counter for a recurring payment + - **recurrence_start** (u32, optional): the optional start period for a recurring payment + - **payer_info** (hex, optional): the payer-provided blob to derive payer_key + - **recurrence_signature** (bip340sig, optional): the payer key signature + +If **type** is "bolt12 invoice_request", and **valid** is *false*: + - the following warnings are possible: + - **warning_invoice_request_missing_offer_id**: No **offer_id** + - **warning_invoice_request_missing_payer_key**: No **payer_key** + - **warning_invoice_request_missing_recurrence_signature**: No **recurrence_signature** + - **warning_invoice_request_invalid_recurrence_signature**: **recurrence_signature** incorrect + +If **type** is "bolt11 invoice", and **valid** is *true*: + - **currency** (string): the BIP173 name for the currency + - **created_at** (u64): the UNIX-style timestamp of the invoice + - **expiry** (u64): the number of seconds this is valid after *timestamp* + - **payee** (pubkey): the public key of the recipient + - **payment_hash** (hex): the hash of the *payment_preimage* (always 64 characters) + - **signature** (signature): signature of the *payee* on this invoice + - **min_final_cltv_expiry** (u32): the minimum CLTV delay for the final node + - **amount_msat** (msat, optional): Amount the invoice asked for + - **description** (string, optional): the description of the purpose of the purchase + - **description_hash** (hex, optional): the hash of the description, in place of *description* (always 64 characters) + - **payment_secret** (hex, optional): the secret to hand to the payee node (always 64 characters) + - **features** (hex, optional): the features bitmap for this invoice + - **fallbacks** (array of objects, optional): onchain addresses: + - **type** (string): the address type (if known) (one of "P2PKH", "P2SH", "P2WPKH", "P2WSH") + - **hex** (hex): Raw encoded address + - **addr** (string, optional): the address in appropriate format for *type* + - **routes** (array of arrays, optional): Route hints to the *payee*: + - hops in the route: + - **pubkey** (pubkey): the public key of the node + - **short_channel_id** (short_channel_id): a channel to the next peer + - **fee_base_msat** (u32): the base fee for payments + - **fee_proportional_millionths** (u32): the parts-per-million fee for payments + - **cltv_expiry_delta** (u32): the CLTV delta across this hop + - **extra** (array of objects, optional): Any extra fields we didn't know how to parse: + - **tag** (string): The bech32 letter which identifies this field (always 1 characters) + - **data** (string): The bech32 data for this field +[comment]: # (GENERATE-FROM-SCHEMA-END) AUTHOR ------ @@ -133,3 +183,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:733d22404e5230882682b846ace92451d8988cf028fa903d735f61b7c61f1c08) diff --git a/doc/lightning-decodepay.7 b/doc/lightning-decodepay.7 index ad51e9d79e69..e491f7348cf5 100644 --- a/doc/lightning-decodepay.7 +++ b/doc/lightning-decodepay.7 @@ -12,46 +12,77 @@ specified by the BOLT 11 specification\. .SH RETURN VALUE -On success, an object is returned with the following fields, as -specified by BOLT11: +On success, an object is returned, containing: .RS .IP \[bu] -\fIcurrency\fR: the BIP173 name for the currency\. +\fBcurrency\fR (string): the BIP173 name for the currency .IP \[bu] -\fItimestamp\fR: the UNIX-style timestamp of the invoice\. +\fBcreated_at\fR (u64): the UNIX-style timestamp of the invoice .IP \[bu] -\fIexpiry\fR: the number of seconds this is valid after \fItimestamp\fR\. +\fBexpiry\fR (u64): the number of seconds this is valid after \fItimestamp\fR .IP \[bu] -\fIpayee\fR: the public key of the recipient\. +\fBpayee\fR (pubkey): the public key of the recipient .IP \[bu] -\fIpayment_hash\fR: the payment hash of the request\. +\fBpayment_hash\fR (hex): the hash of the \fIpayment_preimage\fR (always 64 characters) .IP \[bu] -\fIsignature\fR: the DER-encoded signature\. +\fBsignature\fR (signature): signature of the \fIpayee\fR on this invoice .IP \[bu] -\fIdescription\fR: the description of the purpose of the purchase (see -below) +\fBmin_final_cltv_expiry\fR (u32): the minimum CLTV delay for the final node +.IP \[bu] +\fBamount_msat\fR (msat, optional): Amount the invoice asked for +.IP \[bu] +\fBdescription\fR (string, optional): the description of the purpose of the purchase +.IP \[bu] +\fBdescription_hash\fR (hex, optional): the hash of the description, in place of \fIdescription\fR (always 64 characters) +.IP \[bu] +\fBpayment_secret\fR (hex, optional): the secret to hand to the payee node (always 64 characters) +.IP \[bu] +\fBfeatures\fR (hex, optional): the features bitmap for this invoice +.IP \[bu] +\fBfallbacks\fR (array of objects, optional): onchain addresses: +.RS +.IP \[bu] +\fBtype\fR (string): the address type (if known) (one of "P2PKH", "P2SH", "P2WPKH", "P2WSH") +.IP \[bu] +\fBhex\fR (hex): Raw encoded address +.IP \[bu] +\fBaddr\fR (string, optional): the address in appropriate format for \fItype\fR .RE -The following fields are optional: - +.IP \[bu] +\fBroutes\fR (array of arrays, optional): Route hints to the \fIpayee\fR: +.RS +.IP \[bu] +hops in the route: .RS .IP \[bu] -\fImsatoshi\fR: the number of millisatoshi requested (if any)\. +\fBpubkey\fR (pubkey): the public key of the node .IP \[bu] -\fIamount_msat\fR: the same as above, with \fImsat\fR appended (if any)\. +\fBshort_channel_id\fR (short_channel_id): a channel to the next peer .IP \[bu] -\fIfallbacks\fR: array of fallback address object containing a \fIhex\fR -string, and both \fItype\fR and \fIaddr\fR if it is recognized as one of -\fIP2PKH\fR, \fIP2SH\fR, \fIP2WPKH\fR, or \fIP2WSH\fR\. +\fBfee_base_msat\fR (u32): the base fee for payments .IP \[bu] -\fIroutes\fR: an array of routes\. Each route is an arrays of objects, -each containing \fIpubkey\fR, \fIshort_channel_id\fR, \fIfee_base_msat\fR, -\fIfee_proportional_millionths\fR and \fIcltv_expiry_delta\fR\. +\fBfee_proportional_millionths\fR (u32): the parts-per-million fee for payments .IP \[bu] -\fIextra\fR: an array of objects representing unknown fields, each with -one-character \fItag\fR and a \fIdata\fR bech32 string\. +\fBcltv_expiry_delta\fR (u32): the CLTV delta across this hop + +.RE + + +.RE + +.IP \[bu] +\fBextra\fR (array of objects, optional): Any extra fields we didn't know how to parse: +.RS +.IP \[bu] +\fBtag\fR (string): The bech32 letter which identifies this field (always 1 characters) +.IP \[bu] +\fBdata\fR (string): The bech32 data for this field + +.RE + .RE @@ -76,4 +107,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:d3944c14e16584577e0336dda8c2689881524717428eb921e23f8831144ec566 +\" SHA256STAMP:1b0e5c34291b3c252c651e952109f57fe68a90770fecaa0eeebf9070ed8c2dad diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index 695315171dc6..786a692ef76a 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -15,28 +15,35 @@ specified by the BOLT 11 specification. RETURN VALUE ------------ -On success, an object is returned with the following fields, as -specified by BOLT11: -- *currency*: the BIP173 name for the currency. -- *timestamp*: the UNIX-style timestamp of the invoice. -- *expiry*: the number of seconds this is valid after *timestamp*. -- *payee*: the public key of the recipient. -- *payment\_hash*: the payment hash of the request. -- *signature*: the DER-encoded signature. -- *description*: the description of the purpose of the purchase (see - below) - -The following fields are optional: -- *msatoshi*: the number of millisatoshi requested (if any). -- *amount\_msat*: the same as above, with *msat* appended (if any). -- *fallbacks*: array of fallback address object containing a *hex* - string, and both *type* and *addr* if it is recognized as one of - *P2PKH*, *P2SH*, *P2WPKH*, or *P2WSH*. -- *routes*: an array of routes. Each route is an arrays of objects, - each containing *pubkey*, *short\_channel\_id*, *fee\_base\_msat*, - *fee\_proportional\_millionths* and *cltv\_expiry\_delta*. -- *extra*: an array of objects representing unknown fields, each with - one-character *tag* and a *data* bech32 string. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **currency** (string): the BIP173 name for the currency +- **created_at** (u64): the UNIX-style timestamp of the invoice +- **expiry** (u64): the number of seconds this is valid after *timestamp* +- **payee** (pubkey): the public key of the recipient +- **payment_hash** (hex): the hash of the *payment_preimage* (always 64 characters) +- **signature** (signature): signature of the *payee* on this invoice +- **min_final_cltv_expiry** (u32): the minimum CLTV delay for the final node +- **amount_msat** (msat, optional): Amount the invoice asked for +- **description** (string, optional): the description of the purpose of the purchase +- **description_hash** (hex, optional): the hash of the description, in place of *description* (always 64 characters) +- **payment_secret** (hex, optional): the secret to hand to the payee node (always 64 characters) +- **features** (hex, optional): the features bitmap for this invoice +- **fallbacks** (array of objects, optional): onchain addresses: + - **type** (string): the address type (if known) (one of "P2PKH", "P2SH", "P2WPKH", "P2WSH") + - **hex** (hex): Raw encoded address + - **addr** (string, optional): the address in appropriate format for *type* +- **routes** (array of arrays, optional): Route hints to the *payee*: + - hops in the route: + - **pubkey** (pubkey): the public key of the node + - **short_channel_id** (short_channel_id): a channel to the next peer + - **fee_base_msat** (u32): the base fee for payments + - **fee_proportional_millionths** (u32): the parts-per-million fee for payments + - **cltv_expiry_delta** (u32): the CLTV delta across this hop +- **extra** (array of objects, optional): Any extra fields we didn't know how to parse: + - **tag** (string): The bech32 letter which identifies this field (always 1 characters) + - **data** (string): The bech32 data for this field +[comment]: # (GENERATE-FROM-SCHEMA-END) Technically, the *description* field is optional if a *description\_hash* field is given, but in this case **decodepay** will @@ -61,3 +68,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:33a160a1d9e56690e59b71c4d9d3e141bf7604c111cd5a5624bda692b85c9026) diff --git a/doc/schemas/decode.schema.json b/doc/schemas/decode.schema.json new file mode 100644 index 000000000000..f76db53578a4 --- /dev/null +++ b/doc/schemas/decode.schema.json @@ -0,0 +1,806 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ "type", "valid" ], + "properties": { + "type": { + "type": "string", + "enum": [ "bolt12 offer", "bolt12 invoice", "bolt12 invoice_request", "bolt11 invoice" ], + "description": "what kind of object it decoded to" + }, + "valid": { + "type": "boolean", + "description": "if this is false, you *MUST* not use the result except for diagnostics!" + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "bolt12 offer" ] + }, + "valid": { + "type": "boolean", + "enum": [ true ] + } + } + }, + "then": { + "required": [ "offer_id", "node_id", "signature", "description" ], + "additionalProperties": false, + "properties": { + "type": { }, + "valid": { }, + "offer_id": { + "type": "hex", + "description": "the id of this offer (merkle hash of non-signature fields)", + "maxLength": 64, + "minLength": 64 + }, + "node_id": { + "type": "pubkey32", + "description": "x-only public key of the offering node" + }, + "signature": { + "type": "bip340sig", + "description": "BIP-340 signature of the *node_id* on this offer" + }, + "chains": { + "type": "array", + "description": "which blockchains this offer is for (missing implies bitcoin mainnet only)", + "items": { + "type": "hex", + "description": "the genesis blockhash", + "maxLength": 64, + "minLength": 64 + } + }, + "currency": { + "type": "string", + "description": "ISO 4217 code of the currency (missing implies Bitcoin)", + "maxLength": 3, + "minLength": 3 + }, + "minor_unit": { + "type": "u32", + "description": "the number of decimal places to apply to amount (if currency known)" + }, + "warning_offer_unknown_currency": { + "type": "string", + "description": "The currency code is unknown (so no **minor_unit**)" + }, + "amount": { + "type": "u64", + "description": "the amount in the *currency* adjusted by *minor_unit*, if any" + }, + "amount_msat": { + "type": "msat", + "description": "the amount in bitcoin (if specified, and no *currency*)" + }, + "send_invoice": { + "type": "boolean", + "description": "present if this is a send_invoice offer", + "enum" : [ true ] + }, + "refund_for": { + "type": "hex", + "description": "the *payment_preimage* of invoice this is a refund for", + "maxLength": 64, + "minLength": 64 + }, + "description": { + "type": "string", + "description": "the description of the purpose of the offer" + }, + "vendor": { + "type": "string", + "description": "the name of the vendor for this offer" + }, + "features": { + "type": "hex", + "description": "the array of feature bits for this offer" + }, + "absolute_expiry": { + "type": "u64", + "description": "UNIX timestamp of when this offer expires" + }, + "paths": { + "type": "array", + "description": "Paths to the destination", + "items": { + "type": "object", + "required": [ "blinding", "path" ], + "additionalProperties": false, + "properties": { + "blinding": { + "type": "pubkey", + "description": "blinding factor for this path" + }, + "path": { + "type": "array", + "description": "an individual path", + "items": { + "type": "object", + "required": [ "node_id", "enctlv" ], + "additionalProperties": false, + "properties": { + "node_id": { + "type": "pubkey", + "description": "node_id of the hop" + }, + "enctlv": { + "type": "hex", + "description": "encrypted TLV entry for this hop" + } + } + } + } + } + } + }, + "quantity_min": { + "type": "u64", + "description": "the minimum quantity" + }, + "quantity_max": { + "type": "u64", + "description": "the maximum quantity" + }, + "recurrence": { + "type": "object", + "description": "how often to this offer should be used", + "required": [ "period", "time_unit" ], + "additionalProperties": false, + "properties": { + "time_unit": { + "type": "u32", + "description": "the BOLT12 time unit" + }, + "time_unit_name": { + "type": "string", + "description": "the name of *time_unit* (if valid)" + }, + "period": { + "type": "u32", + "description": "how many *time_unit* per payment period" + }, + "basetime": { + "type": "u64", + "description": "period starts at this UNIX timestamp" + }, + "start_any_period": { + "type": "u64", + "description": "you can start at any period (only if **basetime** present)" + }, + "limit": { + "type": "u32", + "description": "maximum period number for recurrence" + }, + "paywindow": { + "type": "object", + "description": "when within a period will payment be accepted (default is prior and during the period)", + "required": [ "seconds_before", "seconds_after" ], + "additionalProperties": false, + "properties": { + "seconds_before": { + "type": "u32", + "description": "seconds prior to period start" + }, + "seconds_after": { + "type": "u32", + "description": "seconds after to period start" + }, + "proportional_amount": { + "type": "boolean", + "enum": [ true ], + "description": "amount should be scaled if payed after period start" + } + } + } + } + } + } + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "bolt12 offer" ] + }, + "valid": { + "type": "boolean", + "enum": [false ] + } + } + }, + "then": { + "required": [ ], + "additionalProperties": false, + "properties": { + "type": { }, + "valid": { }, + "offer_id": { }, + "node_id": { }, + "signature": { }, + "chains": { }, + "currency": { }, + "minor_unit": { }, + "warning_offer_unknown_currency": { }, + "amount": { }, + "amount_msat": { }, + "send_invoice": { }, + "refund_for": { }, + "description": { }, + "vendor": { }, + "features": { }, + "absolute_expiry": { }, + "paths": { }, + "quantity_min": { }, + "quantity_max": { }, + "recurrence": { }, + "warning_offer_missing_description": { + "type": "string", + "description": "No **description**" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "bolt12 invoice" ] + }, + "valid": { + "type": "boolean", + "enum": [ true ] + } + } + }, + "then": { + "required": [ "node_id", "signature", "amount_msat", "description", "timestamp", "payment_hash", "relative_expiry", "min_final_cltv_expiry" ], + "additionalProperties": false, + "properties": { + "type": { }, + "valid": { }, + "offer_id": { + "type": "hex", + "description": "the id of this offer (merkle hash of non-signature fields)", + "maxLength": 64, + "minLength": 64 + }, + "node_id": { + "type": "pubkey32", + "description": "x-only public key of the offering node" + }, + "signature": { + "type": "bip340sig", + "description": "BIP-340 signature of the *node_id* on this offer" + }, + "chains": { + "type": "array", + "description": "which blockchains this offer is for (missing implies bitcoin mainnet only)", + "items": { + "type": "hex", + "description": "the genesis blockhash", + "maxLength": 64, + "minLength": 64 + } + }, + "amount_msat": { + "type": "msat", + "description": "the amount in bitcoin" + }, + "send_invoice": { + "type": "boolean", + "description": "present if this offer was a send_invoice offer", + "enum" : [ true ] + }, + "refund_for": { + "type": "hex", + "description": "the *payment_preimage* of invoice this is a refund for", + "maxLength": 64, + "minLength": 64 + }, + "description": { + "type": "string", + "description": "the description of the purpose of the offer" + }, + "vendor": { + "type": "string", + "description": "the name of the vendor for this offer" + }, + "features": { + "type": "hex", + "description": "the array of feature bits for this offer" + }, + "paths": { + "type": "array", + "description": "Paths to the destination", + "items": { + "type": "object", + "required": [ "blinding", "path" ], + "additionalProperties": false, + "properties": { + "blinding": { + "type": "pubkey", + "description": "blinding factor for this path" + }, + "path": { + "type": "array", + "description": "an individual path", + "items": { + "type": "object", + "required": [ "node_id", "enctlv" ], + "additionalProperties": false, + "properties": { + "node_id": { + "type": "pubkey", + "description": "node_id of the hop" + }, + "enctlv": { + "type": "hex", + "description": "encrypted TLV entry for this hop" + } + } + } + } + } + } + }, + "quantity": { + "type": "u64", + "description": "the quantity ordered" + }, + "recurrence_counter": { + "type": "u32", + "description": "the 0-based counter for a recurring payment" + }, + "recurrence_start": { + "type": "u32", + "description": "the optional start period for a recurring payment" + }, + "recurrence_basetime": { + "type": "u32", + "description": "the UNIX timestamp of the first recurrence period start" + }, + "payer_key": { + "type": "pubkey32", + "description": "the transient key which identifies the payer" + }, + "payer_info": { + "type": "hex", + "description": "the payer-provided blob to derive payer_key" + }, + "timestamp": { + "type": "u64", + "description": "the UNIX timestamp of the invoice" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage*", + "maxLength": 64, + "minLength": 64 + }, + "relative_expiry": { + "type": "u32", + "description": "the number of seconds after *timestamp* when this expires" + }, + "min_final_cltv_expiry": { + "type": "u32", + "description": "the number of blocks required by destination" + }, + "fallbacks": { + "type": "array", + "description": "onchain addresses", + "items": { + "type": "object", + "required": ["version", "hex"], + "additionalProperties": false, + "properties": { + "version": { + "type": "u8", + "description": "Segwit address version" + }, + "hex": { + "type": "hex", + "description": "Raw encoded segwit address" + }, + "address": { + "type": "string", + "description": "bech32 segwit address" + } + } + } + }, + "refund_signature": { + "type": "bip340sig", + "description": "the payer key signature to get a refund" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "bolt12 invoice" ] + }, + "valid": { + "type": "boolean", + "enum": [ false ] + } + } + }, + "then": { + "required": [ ], + "additionalProperties": false, + "properties": { + "type": { }, + "valid": { }, + "offer_id": { }, + "node_id": { }, + "signature": { }, + "chains": { }, + "amount_msat": { }, + "send_invoice": { }, + "refund_for": { }, + "description": { }, + "vendor": { }, + "features": { }, + "paths": { }, + "quantity": { }, + "recurrence_counter": { }, + "recurrence_start": { }, + "recurrence_basetime": { }, + "payer_key": { }, + "payer_info": { }, + "timestamp": { }, + "payment_hash": { }, + "relative_expiry": { }, + "min_final_cltv_expiry": { }, + "fallbacks": { }, + "refund_signature": { }, + "warning_invoice_missing_amount": { + "type": "string", + "description": "**amount_msat* missing" + }, + "warning_invoice_missing_description": { + "type": "string", + "description": "No **description**" + }, + "warning_invoice_missing_blinded_payinfo": { + "type": "string", + "description": "Has **paths** without payinfo" + }, + "warning_invoice_invalid_blinded_payinfo": { + "type": "string", + "description": "Does not have exactly one payinfo for each of **paths**" + }, + "warning_invoice_missing_recurrence_basetime": { + "type": "string", + "description": "Has **recurrence_counter** without **recurrence_basetime**" + }, + "warning_invoice_missing_timestamp": { + "type": "string", + "description": "Missing **timestamp**" + }, + "warning_invoice_missing_payment_hash": { + "type": "string", + "description": "Missing **payment_hash**" + }, + "warning_invoice_refund_signature_missing_payer_key": { + "type": "string", + "description": "Missing **payer_key** for refund_signature" + }, + "warning_invoice_refund_signature_invalid": { + "type": "string", + "description": "**refund_signature** incorrect" + }, + "warning_invoice_refund_missing_signature": { + "type": "string", + "description": "No **refund_signature**" + }, + "fallbacks": { + "type": "array", + "items": { + "type": "object", + "required": ["version", "hex"], + "properties": { + "version": { }, + "hex": { }, + "address": { }, + "warning_invoice_fallbacks_version_invalid": { + "type": "string", + "description": "**version** is > 16" + } + } + } + } + } + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "bolt12 invoice_request" ] + }, + "valid": { + "type": "boolean", + "enum": [ true ] + } + } + }, + "then": { + "required": [ "offer_id", "payer_key" ], + "additionalProperties": false, + "properties": { + "type": { }, + "valid": { }, + "offer_id": { + "type": "hex", + "description": "the id of this offer (merkle hash of non-signature fields)", + "maxLength": 64, + "minLength": 64 + }, + "chains": { + "type": "array", + "description": "which blockchains this offer is for (missing implies bitcoin mainnet only)", + "items": { + "type": "hex", + "description": "the genesis blockhash", + "maxLength": 64, + "minLength": 64 + } + }, + "amount_msat": { + "type": "msat", + "description": "the amount in bitcoin" + }, + "features": { + "type": "hex", + "description": "the array of feature bits for this offer" + }, + "quantity": { + "type": "u64", + "description": "the quantity ordered" + }, + "recurrence_counter": { + "type": "u32", + "description": "the 0-based counter for a recurring payment" + }, + "recurrence_start": { + "type": "u32", + "description": "the optional start period for a recurring payment" + }, + "payer_key": { + "type": "pubkey32", + "description": "the transient key which identifies the payer" + }, + "payer_info": { + "type": "hex", + "description": "the payer-provided blob to derive payer_key" + }, + "recurrence_signature": { + "type": "bip340sig", + "description": "the payer key signature" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "bolt12 invoice_request" ] + }, + "valid": { + "type": "boolean", + "enum": [ false ] + } + } + }, + "then": { + "required": [ ], + "additionalProperties": false, + "properties": { + "type": { }, + "valid": { }, + "offer_id": { }, + "chains": { }, + "amount_msat": { }, + "features": { }, + "quantity": { }, + "recurrence_counter": { }, + "recurrence_start": { }, + "payer_key": { }, + "payer_info": { }, + "recurrence_signature": { }, + "warning_invoice_request_missing_offer_id": { + "type": "string", + "description": "No **offer_id**" + }, + "warning_invoice_request_missing_payer_key": { + "type": "string", + "description": "No **payer_key**" + }, + "warning_invoice_request_missing_recurrence_signature": { + "type": "string", + "description": "No **recurrence_signature**" + }, + "warning_invoice_request_invalid_recurrence_signature": { + "type": "string", + "description": "**recurrence_signature** incorrect" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ "bolt11 invoice" ] + }, + "valid": { + "type": "boolean", + "enum": [ true ] + } + } + }, + "then": { + "required": [ "currency", "created_at", "expiry", "payee", "min_final_cltv_expiry", "payment_hash", "signature" ], + "additionalProperties": false, + "properties": { + "currency": { + "type": "string", + "description": "the BIP173 name for the currency" + }, + "created_at": { + "type": "u64", + "description": "the UNIX-style timestamp of the invoice" + }, + "expiry": { + "type": "u64", + "description": "the number of seconds this is valid after *timestamp*" + }, + "payee": { + "type": "pubkey", + "description": "the public key of the recipient" + }, + "msatoshi": { + "type": "u64", + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "Amount the invoice asked for" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage*", + "maxLength": 64, + "minLength": 64 + }, + "signature": { + "type": "signature", + "description": "signature of the *payee* on this invoice" + }, + "description": { + "type": "string", + "description": "the description of the purpose of the purchase" + }, + "description_hash": { + "type": "hex", + "description": "the hash of the description, in place of *description*", + "maxLength": 64, + "minLength": 64 + }, + "min_final_cltv_expiry": { + "type": "u32", + "description": "the minimum CLTV delay for the final node" + }, + "payment_secret": { + "type": "hex", + "description": "the secret to hand to the payee node", + "maxLength": 64, + "minLength": 64 + }, + "features": { + "type": "hex", + "description": "the features bitmap for this invoice" + }, + "fallbacks": { + "type": "array", + "description": "onchain addresses", + "items": { + "type": "object", + "required": ["type", "hex"], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "the address type (if known)", + "enum": [ "P2PKH", "P2SH", "P2WPKH", "P2WSH" ] + }, + "addr": { + "type": "string", + "description": "the address in appropriate format for *type*" + }, + "hex": { + "type": "hex", + "description": "Raw encoded address" + } + } + } + }, + "routes": { + "type": "array", + "description": "Route hints to the *payee*", + "items": { + "type": "array", + "description": "hops in the route", + "items": { + "type": "object", + "required": [ "pubkey", "short_channel_id", "fee_base_msat", "fee_proportional_millionths", "cltv_expiry_delta" ], + "additionalProperties": false, + "properties": { + "pubkey": { + "type": "pubkey", + "description": "the public key of the node" + }, + "short_channel_id": { + "type": "short_channel_id", + "description": "a channel to the next peer" + }, + "fee_base_msat": { + "type": "u32", + "description": "the base fee for payments" + }, + "fee_proportional_millionths": { + "type": "u32", + "description": "the parts-per-million fee for payments" + }, + "cltv_expiry_delta": { + "type": "u32", + "description": "the CLTV delta across this hop" + } + } + } + } + }, + "extra": { + "type": "array", + "description": "Any extra fields we didn't know how to parse", + "items": { + "type": "object", + "required": [ "tag", "data" ], + "additionalProperties": false, + "properties": { + "tag": { + "type": "string", + "description": "The bech32 letter which identifies this field", + "maxLength": 1, + "minLength": 1 + }, + "data": { + "type": "string", + "description": "The bech32 data for this field" + } + } + } + } + } + } + } + ] +} diff --git a/doc/schemas/decodepay.schema.json b/doc/schemas/decodepay.schema.json new file mode 100644 index 000000000000..9494042936c6 --- /dev/null +++ b/doc/schemas/decodepay.schema.json @@ -0,0 +1,146 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ "currency", "created_at", "expiry", "payee", "min_final_cltv_expiry", "payment_hash", "signature" ], + "additionalProperties": false, + "properties": { + "currency": { + "type": "string", + "description": "the BIP173 name for the currency" + }, + "created_at": { + "type": "u64", + "description": "the UNIX-style timestamp of the invoice" + }, + "expiry": { + "type": "u64", + "description": "the number of seconds this is valid after *timestamp*" + }, + "payee": { + "type": "pubkey", + "description": "the public key of the recipient" + }, + "msatoshi": { + "type": "u64", + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "Amount the invoice asked for" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage*", + "maxLength": 64, + "minLength": 64 + }, + "signature": { + "type": "signature", + "description": "signature of the *payee* on this invoice" + }, + "description": { + "type": "string", + "description": "the description of the purpose of the purchase" + }, + "description_hash": { + "type": "hex", + "description": "the hash of the description, in place of *description*", + "maxLength": 64, + "minLength": 64 + }, + "min_final_cltv_expiry": { + "type": "u32", + "description": "the minimum CLTV delay for the final node" + }, + "payment_secret": { + "type": "hex", + "description": "the secret to hand to the payee node", + "maxLength": 64, + "minLength": 64 + }, + "features": { + "type": "hex", + "description": "the features bitmap for this invoice" + }, + "fallbacks": { + "type": "array", + "description": "onchain addresses", + "items": { + "type": "object", + "required": ["type", "hex"], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "the address type (if known)", + "enum": [ "P2PKH", "P2SH", "P2WPKH", "P2WSH" ] + }, + "addr": { + "type": "string", + "description": "the address in appropriate format for *type*" + }, + "hex": { + "type": "hex", + "description": "Raw encoded address" + } + } + } + }, + "routes": { + "type": "array", + "description": "Route hints to the *payee*", + "items": { + "type": "array", + "description": "hops in the route", + "items": { + "type": "object", + "required": [ "pubkey", "short_channel_id", "fee_base_msat", "fee_proportional_millionths", "cltv_expiry_delta" ], + "additionalProperties": false, + "properties": { + "pubkey": { + "type": "pubkey", + "description": "the public key of the node" + }, + "short_channel_id": { + "type": "short_channel_id", + "description": "a channel to the next peer" + }, + "fee_base_msat": { + "type": "u32", + "description": "the base fee for payments" + }, + "fee_proportional_millionths": { + "type": "u32", + "description": "the parts-per-million fee for payments" + }, + "cltv_expiry_delta": { + "type": "u32", + "description": "the CLTV delta across this hop" + } + } + } + } + }, + "extra": { + "type": "array", + "description": "Any extra fields we didn't know how to parse", + "items": { + "type": "object", + "required": [ "tag", "data" ], + "additionalProperties": false, + "properties": { + "tag": { + "type": "string", + "description": "The bech32 letter which identifies this field", + "maxLength": 1, + "minLength": 1 + }, + "data": { + "type": "string", + "description": "The bech32 data for this field" + } + } + } + } + } +} From fbc26720746bd818f51020eee122a00b45d8f68e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 15:25:01 +0930 Subject: [PATCH 17/62] doc/schemas: delexpiredinvoice, delinvoice, delpay. Signed-off-by: Rusty Russell --- doc/lightning-delexpiredinvoice.7 | 2 +- doc/lightning-delexpiredinvoice.7.md | 3 + doc/lightning-delinvoice.7 | 43 +++++++++++- doc/lightning-delinvoice.7.md | 22 ++++++- doc/lightning-delpay.7 | 39 ++++++++++- doc/lightning-delpay.7.md | 23 ++++++- doc/schemas/delexpiredinvoice.schema.json | 8 +++ doc/schemas/delinvoice.schema.json | 79 +++++++++++++++++++++++ doc/schemas/delpay.schema.json | 75 +++++++++++++++++++++ 9 files changed, 283 insertions(+), 11 deletions(-) create mode 100644 doc/schemas/delexpiredinvoice.schema.json create mode 100644 doc/schemas/delinvoice.schema.json create mode 100644 doc/schemas/delpay.schema.json diff --git a/doc/lightning-delexpiredinvoice.7 b/doc/lightning-delexpiredinvoice.7 index a71442906cfc..680de0f1210f 100644 --- a/doc/lightning-delexpiredinvoice.7 +++ b/doc/lightning-delexpiredinvoice.7 @@ -30,4 +30,4 @@ ZmnSCPxj \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:f7cd68f4d2ef071b45ee705da17a1180775096a2cc64c370822c8c610d1a3979 +\" SHA256STAMP:a47d47609d9b39bbe6c0f0c61e28d255ceb496a49ad306db22ef23011c6f8cb8 diff --git a/doc/lightning-delexpiredinvoice.7.md b/doc/lightning-delexpiredinvoice.7.md index ecf22b3cce15..0f12ecbb8680 100644 --- a/doc/lightning-delexpiredinvoice.7.md +++ b/doc/lightning-delexpiredinvoice.7.md @@ -18,7 +18,9 @@ deleted. RETURN VALUE ------------ +[comment]: # (GENERATE-FROM-SCHEMA-START) On success, an empty object is returned. +[comment]: # (GENERATE-FROM-SCHEMA-END) AUTHOR ------ @@ -35,3 +37,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:f267fd509a5e3e55e2322ddc8b233eb820638ed5f50f606e3e6c8ae17f1c8421) diff --git a/doc/lightning-delinvoice.7 b/doc/lightning-delinvoice.7 index 876fbde1add0..ef620be23636 100644 --- a/doc/lightning-delinvoice.7 +++ b/doc/lightning-delinvoice.7 @@ -16,9 +16,46 @@ The caller should be particularly aware of the error case caused by the .SH RETURN VALUE -On success, an invoice description will be returned as per -\fBlightning-listinvoice\fR(7)\. +Note: The return is the same as an object from \fBlightning-listinvoice\fR(7)\. + +On success, an object is returned, containing: + +.RS +.IP \[bu] +\fBlabel\fR (string): Unique label given at creation time +.IP \[bu] +\fBstatus\fR (string): State of invoice (one of "paid", "expired", "unpaid") +.IP \[bu] +\fBexpires_at\fR (u64): UNIX timestamp when invoice expires (or expired) +.IP \[bu] +\fBbolt11\fR (string, optional): BOLT11 string +.IP \[bu] +\fBbolt12\fR (string, optional): BOLT12 string + +.RE + +If \fBbolt12\fR is present: + +.RS +.IP \[bu] +\fBlocal_offer_id\fR (hex, optional): offer for which this invoice was created + +.RE + +If \fBstatus\fR is "paid": + +.RS +.IP \[bu] +\fBpay_index\fR (u64): unique index for this invoice payment +.IP \[bu] +\fBamount_received_msat\fR (msat): how much was actually received +.IP \[bu] +\fBpaid_at\fR (u64): UNIX timestamp of when payment was received +.IP \[bu] +\fBpayment_preimage\fR (hex): SHA256 of this is the \fIpayment_hash\fR offered in the invoice (always 64 characters) + +.RE .SH ERRORS The following errors may be reported: @@ -50,4 +87,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:92ad4501574b253334bb570960d1835c8bd84483e318ea058e6b565d34109832 +\" SHA256STAMP:28d1e97fe8e8925008b20815c55db569b7ed8dad33d958c0914945c71fb9d654 diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index 96feb506d767..c699bc7de2fb 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -18,8 +18,25 @@ The caller should be particularly aware of the error case caused by the RETURN VALUE ------------ -On success, an invoice description will be returned as per -lightning-listinvoice(7). +Note: The return is the same as an object from lightning-listinvoice(7). + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **label** (string): Unique label given at creation time +- **status** (string): State of invoice (one of "paid", "expired", "unpaid") +- **expires_at** (u64): UNIX timestamp when invoice expires (or expired) +- **bolt11** (string, optional): BOLT11 string +- **bolt12** (string, optional): BOLT12 string + +If **bolt12** is present: + - **local_offer_id** (hex, optional): offer for which this invoice was created + +If **status** is "paid": + - **pay_index** (u64): unique index for this invoice payment + - **amount_received_msat** (msat): how much was actually received + - **paid_at** (u64): UNIX timestamp of when payment was received + - **payment_preimage** (hex): SHA256 of this is the *payment_hash* offered in the invoice (always 64 characters) +[comment]: # (GENERATE-FROM-SCHEMA-END) ERRORS ------ @@ -51,3 +68,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:8cd84ec57d229dacb6d6c52510334da87846f1c8eea7db286063a2513e8318cb) diff --git a/doc/lightning-delpay.7 b/doc/lightning-delpay.7 index 356bf9862169..2a20c2445c68 100644 --- a/doc/lightning-delpay.7 +++ b/doc/lightning-delpay.7 @@ -33,10 +33,43 @@ Only deletes if the payment status matches\. .fi .SH RETURN VALUE -If successful the command returns a payment object, in the same format as \fBlistsendpays\fR\. If the payment is a multi-part payment (MPP) the command return a list of -payments will be return -- one payment object for each partid\. +The returned format is the same as \fBlightning-listsendpays\fR(7)\. If the +payment is a multi-part payment (MPP) the command return a list of +payments will be returned -- one payment object for each partid\. +On success, an object containing \fBpayments\fR is returned\. It is an array of objects, where each object contains: + +.RS +.IP \[bu] +\fBid\fR (u64): unique ID for this payment attempt +.IP \[bu] +\fBpayment_hash\fR (hex): the hash of the \fIpayment_preimage\fR which will prove payment (always 64 characters) +.IP \[bu] +\fBstatus\fR (string): status of the payment (one of "pending", "failed", "complete") +.IP \[bu] +\fBamount_sent_msat\fR (msat): the amount we actually sent, including fees +.IP \[bu] +\fBcreated_at\fR (u64): the UNIX timestamp showing when this payment was initiated +.IP \[bu] +\fBpartid\fR (u64, optional): unique ID within this (multi-part) payment +.IP \[bu] +\fBdestination\fR (pubkey, optional): the final destination of the payment if known +.IP \[bu] +\fBamount_msat\fR (msat, optional): the amount the destination received, if known +.IP \[bu] +\fBpayment_preimage\fR (hex, optional): proof of payment (always 64 characters) +.IP \[bu] +\fBlabel\fR (string, optional): the label, if given to sendpay +.IP \[bu] +\fBbolt11\fR (string, optional): the bolt11 string (if pay supplied one) +.IP \[bu] +\fBbolt12\fR (string, optional): the bolt12 string (if supplied for pay: \fBexperimental-offers\fR only)\. +.IP \[bu] +\fBerroronion\fR (hex, optional): the error onion returned on failure, if any\. + +.RE + On failure, an error is returned\. If the lightning process fails before responding, the caller should use \fBlightning-listsentpays\fR(7) or \fBlightning-listpays\fR(7) to query whether this payment was deleted or not\. @@ -87,4 +120,4 @@ Vincenzo Palazzo \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:b10dd430aaace8b9f3607e72c871e2a883934f9a51d5bc0068a024df5ef1d6ee +\" SHA256STAMP:520f0b56c7288ed46509a0ab90be0959b3b1489b56b7e94f223de19cf7028758 diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index 8c9a78f27c45..d3c7094a45fe 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -31,8 +31,26 @@ EXAMPLE JSON REQUEST RETURN VALUE ------------ -If successful the command returns a payment object, in the same format as **listsendpays**. If the payment is a multi-part payment (MPP) the command return a list of -payments will be return -- one payment object for each partid. +The returned format is the same as lightning-listsendpays(7). If the +payment is a multi-part payment (MPP) the command return a list of +payments will be returned -- one payment object for each partid. + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **payments** is returned. It is an array of objects, where each object contains: +- **id** (u64): unique ID for this payment attempt +- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **status** (string): status of the payment (one of "pending", "failed", "complete") +- **amount_sent_msat** (msat): the amount we actually sent, including fees +- **created_at** (u64): the UNIX timestamp showing when this payment was initiated +- **partid** (u64, optional): unique ID within this (multi-part) payment +- **destination** (pubkey, optional): the final destination of the payment if known +- **amount_msat** (msat, optional): the amount the destination received, if known +- **payment_preimage** (hex, optional): proof of payment (always 64 characters) +- **label** (string, optional): the label, if given to sendpay +- **bolt11** (string, optional): the bolt11 string (if pay supplied one) +- **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). +- **erroronion** (hex, optional): the error onion returned on failure, if any. +[comment]: # (GENERATE-FROM-SCHEMA-END) On failure, an error is returned. If the lightning process fails before responding, the caller should use lightning-listsentpays(7) or lightning-listpays(7) to query whether this payment was deleted or not. @@ -81,3 +99,4 @@ RESOURCES --------- Main web site: +[comment]: # ( SHA256STAMP:26e293e5b3de31a95572763a6d7c360c0f9f78112b3fcef12c639d001b0fa9b5) diff --git a/doc/schemas/delexpiredinvoice.schema.json b/doc/schemas/delexpiredinvoice.schema.json new file mode 100644 index 000000000000..3f3efd17a47a --- /dev/null +++ b/doc/schemas/delexpiredinvoice.schema.json @@ -0,0 +1,8 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ ], + "additionalProperties": false, + "properties": { + } +} diff --git a/doc/schemas/delinvoice.schema.json b/doc/schemas/delinvoice.schema.json new file mode 100644 index 000000000000..846237a30951 --- /dev/null +++ b/doc/schemas/delinvoice.schema.json @@ -0,0 +1,79 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ "label", "payment_hash", "amount_msat", "status", "expires_at" ], + "additionalProperties": true, + "properties": { + "label": { + "type": "string", + "description": "Unique label given at creation time" + }, + "bolt11": { + "type": "string", + "description": "BOLT11 string" + }, + "bolt12": { + "type": "string", + "description": "BOLT12 string" + }, + "status": { + "type": "string", + "description": "State of invoice", + "enum": [ "paid", "expired", "unpaid" ] + }, + "expires_at": { + "type": "u64", + "description": "UNIX timestamp when invoice expires (or expired)" + } + }, + "allOf": [ + { + "if": { + "required": [ "bolt12" ] + }, + "then": { + "required": [ ], + "properties": { + "local_offer_id": { + "type": "hex", + "description": "offer for which this invoice was created" + } + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ "paid" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "pay_index", "amount_received_msat", "paid_at", "payment_preimage" ], + "properties": { + "pay_index": { + "type": "u64", + "description": "unique index for this invoice payment" + }, + "amount_received_msat": { + "type": "msat", + "description": "how much was actually received" + }, + "paid_at": { + "type": "u64", + "description": "UNIX timestamp of when payment was received" + }, + "payment_preimage": { + "type": "hex", + "description": "SHA256 of this is the *payment_hash* offered in the invoice", + "maxLength": 64, + "minLength": 64 + } + } + } + } + ] +} diff --git a/doc/schemas/delpay.schema.json b/doc/schemas/delpay.schema.json new file mode 100644 index 000000000000..3a1ed1ba633e --- /dev/null +++ b/doc/schemas/delpay.schema.json @@ -0,0 +1,75 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ "payments" ], + "additionalProperties": false, + "properties": { + "payments": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ "id", "payment_hash", "status", "amount_sent_msat", "created_at" ], + "properties": { + "id": { + "type": "u64", + "description": "unique ID for this payment attempt" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ "pending", "failed", "complete" ], + "description": "status of the payment" + }, + "amount_sent_msat": { + "type": "msat", + "description": "the amount we actually sent, including fees" + }, + "partid": { + "type": "u64", + "description": "unique ID within this (multi-part) payment" + }, + "destination": { + "type": "pubkey", + "description": "the final destination of the payment if known" + }, + "amount_msat": { + "type": "msat", + "description": "the amount the destination received, if known" + }, + "created_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "payment_preimage": { + "type": "hex", + "description": "proof of payment", + "maxLength": 64, + "minLength": 64 + }, + "label": { + "type": "string", + "description": "the label, if given to sendpay" + }, + "bolt11": { + "type": "string", + "description": "the bolt11 string (if pay supplied one)" + }, + "bolt12": { + "type": "string", + "description": "the bolt12 string (if supplied for pay: **experimental-offers** only)." + }, + "erroronion": { + "type": "hex", + "description": "the error onion returned on failure, if any." + } + } + } + } + } +} From ec83d7a8a5c6ebb2c01e6985400b7bbe0ab8c8ae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 May 2021 17:05:01 +0930 Subject: [PATCH 18/62] doc/schemas: disableoffer, disconnect, feerates, fetchinvoice, fundchannel, fundchannel_cancel, fundchannel_complete, fundchannel_start, fundpsbt, getinfo, getlog, getroute. We also add a test for getlog, since it was never called by the testsuite. Signed-off-by: Rusty Russell --- doc/lightning-disableoffer.7 | 23 +++- doc/lightning-disableoffer.7.md | 14 +- doc/lightning-disconnect.7 | 2 +- doc/lightning-disconnect.7.md | 3 + doc/lightning-feerates.7 | 94 ++++++++------ doc/lightning-feerates.7.md | 72 +++++------ doc/lightning-fetchinvoice.7 | 50 +++----- doc/lightning-fetchinvoice.7.md | 40 +++--- doc/lightning-fundchannel.7 | 23 ++-- doc/lightning-fundchannel.7.md | 17 +-- doc/lightning-fundchannel_cancel.7 | 10 +- doc/lightning-fundchannel_cancel.7.md | 7 +- doc/lightning-fundchannel_complete.7 | 12 +- doc/lightning-fundchannel_complete.7.md | 8 +- doc/lightning-fundchannel_start.7 | 15 ++- doc/lightning-fundchannel_start.7.md | 10 +- doc/lightning-fundpsbt.7 | 2 +- doc/lightning-fundpsbt.7.md | 1 + doc/lightning-getinfo.7 | 60 ++++++--- doc/lightning-getinfo.7.md | 46 ++++--- doc/lightning-getlog.7 | 54 ++++++-- doc/lightning-getlog.7.md | 37 ++++-- doc/lightning-getroute.7 | 28 ++-- doc/lightning-getroute.7.md | 20 ++- doc/schemas/disableoffer.schema.json | 35 +++++ doc/schemas/disconnect.schema.json | 7 + doc/schemas/feerates.schema.json | 119 +++++++++++++++++ doc/schemas/fetchinvoice.schema.json | 68 ++++++++++ doc/schemas/fundchannel.schema.json | 30 +++++ doc/schemas/fundchannel_cancel.schema.json | 12 ++ doc/schemas/fundchannel_complete.schema.json | 19 +++ doc/schemas/fundchannel_start.schema.json | 20 +++ doc/schemas/fundpsbt.schema.json | 61 +++++++++ doc/schemas/getinfo.schema.json | 124 ++++++++++++++++++ doc/schemas/getlog.schema.json | 127 +++++++++++++++++++ doc/schemas/getroute.schema.json | 47 +++++++ tests/test_misc.py | 13 ++ 37 files changed, 1083 insertions(+), 247 deletions(-) create mode 100644 doc/schemas/disableoffer.schema.json create mode 100644 doc/schemas/disconnect.schema.json create mode 100644 doc/schemas/feerates.schema.json create mode 100644 doc/schemas/fetchinvoice.schema.json create mode 100644 doc/schemas/fundchannel.schema.json create mode 100644 doc/schemas/fundchannel_cancel.schema.json create mode 100644 doc/schemas/fundchannel_complete.schema.json create mode 100644 doc/schemas/fundchannel_start.schema.json create mode 100644 doc/schemas/fundpsbt.schema.json create mode 100644 doc/schemas/getinfo.schema.json create mode 100644 doc/schemas/getlog.schema.json create mode 100644 doc/schemas/getroute.schema.json diff --git a/doc/lightning-disableoffer.7 b/doc/lightning-disableoffer.7 index 1b1547e38bed..88f923c77d86 100644 --- a/doc/lightning-disableoffer.7 +++ b/doc/lightning-disableoffer.7 @@ -33,9 +33,26 @@ forgotten entirely (there may be invoices which refer to this offer)\. .fi .SH RETURN VALUE -If successful the command returns an object, in the same format as \fBlistoffers\fR\. -The "active" field will always be false\. +Note: the returned object is the same format as \fBlistoffers\fR\. + +On success, an object is returned, containing: + +.RS +.IP \[bu] +\fBoffer_id\fR (hex): the merkle hash of the offer (always 64 characters) +.IP \[bu] +\fBactive\fR (boolean): Whether the offer can produce invoices/payments (always "false") +.IP \[bu] +\fBsingle_use\fR (boolean): Whether the offer is disabled after first successful use +.IP \[bu] +\fBbolt12\fR (string): The bolt12 string representing this offer +.IP \[bu] +\fBused\fR (boolean): Whether the offer has had an invoice paid / payment made +.IP \[bu] +\fBlabel\fR (string, optional): The label provided when offer was created + +.RE .SH EXAMPLE JSON RESPONSE .nf .RS @@ -61,4 +78,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:55cfb050fa2d28f58750dc74659d9f7bc5ec00499e218ccffd058db51efc38d2 +\" SHA256STAMP:33b8cddae4a830e710906268c1d0d301442bc7ab0711e1d6ce670f86c77ae320 diff --git a/doc/lightning-disableoffer.7.md b/doc/lightning-disableoffer.7.md index 398fd04387ce..201cd99c5f91 100644 --- a/doc/lightning-disableoffer.7.md +++ b/doc/lightning-disableoffer.7.md @@ -32,8 +32,17 @@ EXAMPLE JSON REQUEST RETURN VALUE ------------ -If successful the command returns an object, in the same format as **listoffers**. -The "active" field will always be false. +Note: the returned object is the same format as **listoffers**. + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **offer_id** (hex): the merkle hash of the offer (always 64 characters) +- **active** (boolean): Whether the offer can produce invoices/payments (always "false") +- **single_use** (boolean): Whether the offer is disabled after first successful use +- **bolt12** (string): The bolt12 string representing this offer +- **used** (boolean): Whether the offer has had an invoice paid / payment made +- **label** (string, optional): The label provided when offer was created +[comment]: # (GENERATE-FROM-SCHEMA-END) EXAMPLE JSON RESPONSE ----- @@ -63,3 +72,4 @@ RESOURCES --------- Main web site: +[comment]: # ( SHA256STAMP:6b0ae21e38a83742735f38e9c022b33ed4a436cacc746ff22e63d7b00779e4d0) diff --git a/doc/lightning-disconnect.7 b/doc/lightning-disconnect.7 index bbea46d36085..5110a38014c2 100644 --- a/doc/lightning-disconnect.7 +++ b/doc/lightning-disconnect.7 @@ -63,4 +63,4 @@ Michael Hawkins \fI\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:fe16ccb06f56e5b59a1aea19b1a6625624b93e668cd07bba9e3f581873fa3eab +\" SHA256STAMP:7efc0fbbb7397d3767ea170ba091b5bc00f4efaf8e1283c7b7ed1aaaccfd3c4d diff --git a/doc/lightning-disconnect.7.md b/doc/lightning-disconnect.7.md index e3bc72a7f450..30571804aa24 100644 --- a/doc/lightning-disconnect.7.md +++ b/doc/lightning-disconnect.7.md @@ -33,7 +33,9 @@ connection. RETURN VALUE ------------ +[comment]: # (GENERATE-FROM-SCHEMA-START) On success, an empty object is returned. +[comment]: # (GENERATE-FROM-SCHEMA-END) On error the returned object will contain `code` and `message` properties, with `code` being one of the following: @@ -56,3 +58,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:f974a3848c4db5b73fffa969a741ef6619c9a375783fabe731882d84a6bbf5ff) diff --git a/doc/lightning-feerates.7 b/doc/lightning-feerates.7 index c6ec64de2a00..294aeaf2fc46 100644 --- a/doc/lightning-feerates.7 +++ b/doc/lightning-feerates.7 @@ -54,61 +54,77 @@ which will override the recommended feerates returned by \fBfeerates\fR\. .SH RETURN VALUE -The \fBfeerates\fR command returns the feerates in an object named -\fIperkw\fR or \fIperkb\fR, depending on your \fIstyle\fR parameter\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBperkb\fR (object, optional): If \fIstyle\fR parameter was perkb: +.RS +.IP \[bu] +\fBmin_acceptable\fR (u32): The smallest feerate that you can use, usually the minimum relayed feerate of the backend +.IP \[bu] +\fBmax_acceptable\fR (u32): The largest feerate we will accept from remote negotiations\. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet)\. +.IP \[bu] +\fBopening\fR (u32, optional): Default feerate for \fBlightning-fundchannel\fR(7) and \fBlightning-withdraw\fR(7) +.IP \[bu] +\fBmutual_close\fR (u32, optional): Feerate to aim for in cooperative shutdown\. Note that since mutual close is a \fBnegotiation\fR, the actual feerate used in mutual close will be somewhere between this and the corresponding mutual close feerate of the peer\. +.IP \[bu] +\fBunilateral_close\fR (u32, optional): Feerate for commitment_transaction in a live channel which we originally funded +.IP \[bu] +\fBdelayed_to_us\fR (u32, optional): Feerate for returning unilateral close funds to our wallet +.IP \[bu] +\fBhtlc_resolution\fR (u32, optional): Feerate for returning unilateral close HTLC outputs to our wallet +.IP \[bu] +\fBpenalty\fR (u32, optional): Feerate to start at when penalizing a cheat attempt -Some of these estimations may be missing, except for \fImin_acceptable\fR -and \fImax_acceptable\fR, which are always present\. +.RE +.IP \[bu] +\fBperkw\fR (object, optional): If \fIstyle\fR parameter was perkw: +.RS +.IP \[bu] +\fBmin_acceptable\fR (u32): The smallest feerate that you can use, usually the minimum relayed feerate of the backend +.IP \[bu] +\fBmax_acceptable\fR (u32): The largest feerate we will accept from remote negotiations\. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet)\. +.IP \[bu] +\fBopening\fR (u32, optional): Default feerate for \fBlightning-fundchannel\fR(7) and \fBlightning-withdraw\fR(7) +.IP \[bu] +\fBmutual_close\fR (u32, optional): Feerate to aim for in cooperative shutdown\. Note that since mutual close is a \fBnegotiation\fR, the actual feerate used in mutual close will be somewhere between this and the corresponding mutual close feerate of the peer\. +.IP \[bu] +\fBunilateral_close\fR (u32, optional): Feerate for commitment_transaction in a live channel which we originally funded +.IP \[bu] +\fBdelayed_to_us\fR (u32, optional): Feerate for returning unilateral close funds to our wallet +.IP \[bu] +\fBhtlc_resolution\fR (u32, optional): Feerate for returning unilateral close HTLC outputs to our wallet +.IP \[bu] +\fBpenalty\fR (u32, optional): Feerate to start at when penalizing a cheat attempt -The \fIperkw\fR or \fIperkb\fR object may have fields containing the estimates: +.RE -.RS .IP \[bu] -\fIopening\fR - feerate used for channel opening by \fBlightning-fundchannel\fR(7), -as well as normal onchain-to-onchain spends by \fBlightning-withdraw\fR(7)\. -In general, for all normal onchain-to-onchain spends, this is the feerate -you should also use\. +\fBonchain_fee_estimates\fR (object, optional): +.RS .IP \[bu] -\fImutual_close\fR - the starting feerate used in mutual close negotiation\. -Note that since mutual close is a \fBnegotiation\fR, -the actual feerate used in mutual close -will be somewhere between this -and the corresponding mutual close feerate of the peer\. +\fBopening_channel_satoshis\fR (u64): Estimated cost of typical channel open .IP \[bu] -\fIunilateral_close\fR - the feerate we will pay for when a unilateral close -is done on a channel we originally funded\. -When anchor commitments are implemented, -this will be the feerate we will use -for a unilateral close we initiated\. +\fBmutual_close_satoshis\fR (u64): Estimated cost of typical channel close .IP \[bu] -\fIdelayed_to_us\fR - the feerate we will use when claiming our output from -a unilateral close we initiated\. +\fBunilateral_close_satoshis\fR (u64): Estimated cost of typical unilateral close (without HTLCs) .IP \[bu] -\fIhtlc_resolution\fR - the feerate we will use to claim HTLCs -from a unilateral close we initiated\. +\fBhtlc_timeout_satoshis\fR (u64): Estimated cost of typical HTLC timeout transaction .IP \[bu] -\fIpenalty\fR - the feerate we will use to revoke old state, -if the counterparty attempts to cheat us\. +\fBhtlc_success_satoshis\fR (u64): Estimated cost of typical HTLC fulfillment transaction .RE -The following fields are always present in the \fIperkw\fR or \fIperkb\fR object: + +.RE + +The following warnings may also be returned: .RS .IP \[bu] -\fImin_acceptable\fR - the smallest feerate that you can use, -usually the minimum relayed feerate of the backend\. -.IP \[bu] -\fImax_acceptable\fR - the largest feerate we will accept -from remote negotiations\. -If a peer attempts to open a channel to us but wants a unilateral close -feerate larger than \fImax_acceptable\fR, we reject the open attempt\. -If the peer attempts to change the unilateral close feerate of a channel it -opened to us, such that the new feerate exceeds \fImax_acceptable\fR, we -unilaterally close the channel -(at the current unilateral close feerate instead of the new one)\. +\fBwarning_missing_feerates\fR: Some fee estimates are missing .RE .SH ERRORS @@ -153,4 +169,4 @@ manpage\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:f0fc7218cc1286cb6fb299cf81553d851d4ba4fee570e7bb169593d17aea951b +\" SHA256STAMP:6e1ae2f26676a76231461ac1f696066e538edabfab00ec9480550a5c6bc6981e diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index 043ab6237a1e..79ae753e669e 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -44,47 +44,36 @@ which will override the recommended feerates returned by **feerates**. RETURN VALUE ------------ -The **feerates** command returns the feerates in an object named -*perkw* or *perkb*, depending on your *style* parameter. - -Some of these estimations may be missing, except for *min\_acceptable* -and *max\_acceptable*, which are always present. - -The *perkw* or *perkb* object may have fields containing the estimates: - -* *opening* - feerate used for channel opening by lightning-fundchannel(7), - as well as normal onchain-to-onchain spends by lightning-withdraw(7). - In general, for all normal onchain-to-onchain spends, this is the feerate - you should also use. -* *mutual\_close* - the starting feerate used in mutual close negotiation. - Note that since mutual close is a **negotiation**, - the actual feerate used in mutual close - will be somewhere between this - and the corresponding mutual close feerate of the peer. -* *unilateral\_close* - the feerate we will pay for when a unilateral close - is done on a channel we originally funded. - When anchor commitments are implemented, - this will be the feerate we will use - for a unilateral close we initiated. -* *delayed\_to\_us* - the feerate we will use when claiming our output from - a unilateral close we initiated. -* *htlc_resolution* - the feerate we will use to claim HTLCs - from a unilateral close we initiated. -* *penalty* - the feerate we will use to revoke old state, - if the counterparty attempts to cheat us. - -The following fields are always present in the *perkw* or *perkb* object: - -* *min\_acceptable* - the smallest feerate that you can use, - usually the minimum relayed feerate of the backend. -* *max\_acceptable* - the largest feerate we will accept - from remote negotiations. - If a peer attempts to open a channel to us but wants a unilateral close - feerate larger than *max\_acceptable*, we reject the open attempt. - If the peer attempts to change the unilateral close feerate of a channel it - opened to us, such that the new feerate exceeds *max\_acceptable*, we - unilaterally close the channel - (at the current unilateral close feerate instead of the new one). +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **perkb** (object, optional): If *style* parameter was perkb: + - **min_acceptable** (u32): The smallest feerate that you can use, usually the minimum relayed feerate of the backend + - **max_acceptable** (u32): The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet). + - **opening** (u32, optional): Default feerate for lightning-fundchannel(7) and lightning-withdraw(7) + - **mutual_close** (u32, optional): Feerate to aim for in cooperative shutdown. Note that since mutual close is a **negotiation**, the actual feerate used in mutual close will be somewhere between this and the corresponding mutual close feerate of the peer. + - **unilateral_close** (u32, optional): Feerate for commitment_transaction in a live channel which we originally funded + - **delayed_to_us** (u32, optional): Feerate for returning unilateral close funds to our wallet + - **htlc_resolution** (u32, optional): Feerate for returning unilateral close HTLC outputs to our wallet + - **penalty** (u32, optional): Feerate to start at when penalizing a cheat attempt +- **perkw** (object, optional): If *style* parameter was perkw: + - **min_acceptable** (u32): The smallest feerate that you can use, usually the minimum relayed feerate of the backend + - **max_acceptable** (u32): The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet). + - **opening** (u32, optional): Default feerate for lightning-fundchannel(7) and lightning-withdraw(7) + - **mutual_close** (u32, optional): Feerate to aim for in cooperative shutdown. Note that since mutual close is a **negotiation**, the actual feerate used in mutual close will be somewhere between this and the corresponding mutual close feerate of the peer. + - **unilateral_close** (u32, optional): Feerate for commitment_transaction in a live channel which we originally funded + - **delayed_to_us** (u32, optional): Feerate for returning unilateral close funds to our wallet + - **htlc_resolution** (u32, optional): Feerate for returning unilateral close HTLC outputs to our wallet + - **penalty** (u32, optional): Feerate to start at when penalizing a cheat attempt +- **onchain_fee_estimates** (object, optional): + - **opening_channel_satoshis** (u64): Estimated cost of typical channel open + - **mutual_close_satoshis** (u64): Estimated cost of typical channel close + - **unilateral_close_satoshis** (u64): Estimated cost of typical unilateral close (without HTLCs) + - **htlc_timeout_satoshis** (u64): Estimated cost of typical HTLC timeout transaction + - **htlc_success_satoshis** (u64): Estimated cost of typical HTLC fulfillment transaction + +The following warnings may also be returned: +- **warning_missing_feerates**: Some fee estimates are missing +[comment]: # (GENERATE-FROM-SCHEMA-END) ERRORS ------ @@ -129,3 +118,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:e16ae963e528995f1e01c80b4ed4e9b0d6c457a559928e98ab6cf32624557894) diff --git a/doc/lightning-fetchinvoice.7 b/doc/lightning-fetchinvoice.7 index ec1223ca1a07..753f4c66ec18 100644 --- a/doc/lightning-fetchinvoice.7 +++ b/doc/lightning-fetchinvoice.7 @@ -47,55 +47,43 @@ we fail (default, 60 seconds)\. .SH RETURN VALUE -On success, an object as follows is returned: +On success, an object is returned, containing: .RS .IP \[bu] -\fIinvoice\fR: the bolt12-encoded invoice string, starting with "lni1"\. +\fBinvoice\fR (string): The BOLT12 invoice we fetched .IP \[bu] -\fIchanges\fR: an object detailing changes between the offer and invoice\. - -.RE - -Optionally: - +\fBchanges\fR (object): Summary of changes from offer: .RS .IP \[bu] -\fInext_period\fR: an object returned for recurring invoices if the next -period is under the recurrence_limit (if any)\. - -.RE - -The \fIchanges\fR object can have and of the following members: - -.RS -.IP \[bu] -\fIdescription_appended\fR: extra characters appended to the \fIdescription\fR field\. +\fBdescription_appended\fR (string, optional): extra characters appended to the \fIdescription\fR field\. .IP \[bu] -\fIdescription\fR: a completely replaced \fIdescription\fR field\. +\fBdescription\fR (string, optional): a completely replaced \fIdescription\fR field .IP \[bu] -\fIvendor_removed\fR": the offer vendor field, which has been omitted from the invoice\. +\fBvendor_removed\fR (string, optional): The \fIvendor\fR from the offer, which is missing in the invoice .IP \[bu] -\fIvendor\fR": the offer vendor field, which has changed from the invoice\. +\fBvendor\fR (string, optional): a completely replaced \fIvendor\fR field .IP \[bu] -\fImsat\fR": the amount, if different from the offer amount multiplied -by any \fIquantity\fR (or the offer had no amount, or was not in BTC)\. +\fBmsat\fR (msat, optional): the amount, if different from the offer amount multiplied by any \fIquantity\fR (or the offer had no amount, or was not in BTC)\. .RE -The \fInext_period\fR object has at least the following members: - +.IP \[bu] +\fBnext_period\fR (object, optional): Only for recurring invoices if the next period is under the \fIrecurrence_limit\fR: .RS .IP \[bu] -\fIcounter\fR: the index of the next period to be fetchinvoice\. +\fBcounter\fR (u64): the index of the next period to fetchinvoice .IP \[bu] -\fIstarttime\fR: the time that the next period starts (seconds since 1970) +\fBstarttime\fR (u64): UNIX timestamp that the next period starts .IP \[bu] -\fIendtime\fR: the time that the next period ends (seconds since 1970) +\fBendtime\fR (u64): UNIX timestamp that the next period ends .IP \[bu] -\fIpaywindow_start\fR: the earliest time that the next invoice can be fetched (seconds since 1970) +\fBpaywindow_start\fR (u64): UNIX timestamp of the earliest time that the next invoice can be fetched .IP \[bu] -\fIpaywindow_end\fR: the latest time that the next invoice can be fetched (seconds since 1970) +\fBpaywindow_end\fR (u64): UNIX timestamp of the latest time that the next invoice can be fetched + +.RE + .RE @@ -126,4 +114,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:a96df8e4f480c093c83268688d683db82575341ecb4176ab586eae9baf69d9c1 +\" SHA256STAMP:23be8c105314c109fa0f7c532c3eebf9b2cbe47f520d600d3d2159765def3cbd diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index de1faafb1316..46de11de5c81 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -42,29 +42,22 @@ we fail (default, 60 seconds). RETURN VALUE ------------ -On success, an object as follows is returned: - -* *invoice*: the bolt12-encoded invoice string, starting with "lni1". -* *changes*: an object detailing changes between the offer and invoice. - -Optionally: -* *next_period*: an object returned for recurring invoices if the next - period is under the recurrence_limit (if any). - -The *changes* object can have and of the following members: -* *description_appended*: extra characters appended to the *description* field. -* *description*: a completely replaced *description* field. -* *vendor_removed*": the offer vendor field, which has been omitted from the invoice. -* *vendor*": the offer vendor field, which has changed from the invoice. -* *msat*": the amount, if different from the offer amount multiplied - by any *quantity* (or the offer had no amount, or was not in BTC). - -The *next_period* object has at least the following members: -* *counter*: the index of the next period to be fetchinvoice. -* *starttime*: the time that the next period starts (seconds since 1970) -* *endtime*: the time that the next period ends (seconds since 1970) -* *paywindow_start*: the earliest time that the next invoice can be fetched (seconds since 1970) -* *paywindow_end*: the latest time that the next invoice can be fetched (seconds since 1970) +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **invoice** (string): The BOLT12 invoice we fetched +- **changes** (object): Summary of changes from offer: + - **description_appended** (string, optional): extra characters appended to the *description* field. + - **description** (string, optional): a completely replaced *description* field + - **vendor_removed** (string, optional): The *vendor* from the offer, which is missing in the invoice + - **vendor** (string, optional): a completely replaced *vendor* field + - **msat** (msat, optional): the amount, if different from the offer amount multiplied by any *quantity* (or the offer had no amount, or was not in BTC). +- **next_period** (object, optional): Only for recurring invoices if the next period is under the *recurrence_limit*: + - **counter** (u64): the index of the next period to fetchinvoice + - **starttime** (u64): UNIX timestamp that the next period starts + - **endtime** (u64): UNIX timestamp that the next period ends + - **paywindow_start** (u64): UNIX timestamp of the earliest time that the next invoice can be fetched + - **paywindow_end** (u64): UNIX timestamp of the latest time that the next invoice can be fetched +[comment]: # (GENERATE-FROM-SCHEMA-END) The following error codes may occur: - -1: Catchall nonspecific error. @@ -88,3 +81,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:e2b81ad7a61dd6a6d55e21c5367c3286aaf00cee734b2719a8e38bc87f7ac8aa) diff --git a/doc/lightning-fundchannel.7 b/doc/lightning-fundchannel.7 index 73860c32aee0..69944ba73735 100644 --- a/doc/lightning-fundchannel.7 +++ b/doc/lightning-fundchannel.7 @@ -86,16 +86,21 @@ lightning-cli -k fundchannel id=03f...fc1 amount=all feerate=normal utxos='["bcc .fi .SH RETURN VALUE -On success, the \fItx\fR and \fItxid\fR of the transaction is returned, as well -as the \fIoutnum\fR indicating the output index which creates the channel, as well -as the \fIchannel_id\fR of the newly created channel\. On failure, an error -is reported and the channel is not funded\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBtx\fR (hex): The raw transaction which funded the channel +.IP \[bu] +\fBtxid\fR (txid): The txid of the transaction which funded the channel +.IP \[bu] +\fBoutnum\fR (u32): The 0-based output index showing which output funded the channel +.IP \[bu] +\fBchannel_id\fR (hex): The channel_id of the resulting channel (always 64 characters) +.IP \[bu] +\fBclose_to\fR (hex, optional): The raw scriptPubkey which mutual close will go to; only present if \fIclose_to\fR parameter was specified and peer supports \fBoption_upfront_shutdown_script\fR -If a \fBclose_to\fR address was provided, will close to this address -iff the \fBclose_to\fR script is returned in the response\. Otherwise, -the peer does not support \fBoption_upfront_shutdownscript\fR\. - +.RE The following error codes may occur: @@ -125,4 +130,4 @@ channel parameters (funding limits, channel reserves, fees, etc\.)\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:6b8356d6c8f33ffa6885060386776334b6ee4eaf051362eed188541e3582e1e5 +\" SHA256STAMP:ee8d7d247d9d4f263f8bbed936a2ba4b61d4afc5c48580f462a0d6142c13dbbd diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index 723914ed326c..deaa3904e8c8 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -73,14 +73,14 @@ This example shows how to use lightning-cli to open new channel with peer 03f... RETURN VALUE ------------ -On success, the *tx* and *txid* of the transaction is returned, as well -as the *outnum* indicating the output index which creates the channel, as well -as the *channel\_id* of the newly created channel. On failure, an error -is reported and the channel is not funded. - -If a `close_to` address was provided, will close to this address -iff the `close_to` script is returned in the response. Otherwise, -the peer does not support `option_upfront_shutdownscript`. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **tx** (hex): The raw transaction which funded the channel +- **txid** (txid): The txid of the transaction which funded the channel +- **outnum** (u32): The 0-based output index showing which output funded the channel +- **channel_id** (hex): The channel_id of the resulting channel (always 64 characters) +- **close_to** (hex, optional): The raw scriptPubkey which mutual close will go to; only present if *close_to* parameter was specified and peer supports `option_upfront_shutdown_script` +[comment]: # (GENERATE-FROM-SCHEMA-END) The following error codes may occur: - -1: Catchall nonspecific error. @@ -103,3 +103,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:5b17c334b90f840a986750f9fcdb6a6bfa79bd1a3da11319a1957ba87bc4b0a7) diff --git a/doc/lightning-fundchannel_cancel.7 b/doc/lightning-fundchannel_cancel.7 index e2c94a58d792..663e39ccdd80 100644 --- a/doc/lightning-fundchannel_cancel.7 +++ b/doc/lightning-fundchannel_cancel.7 @@ -25,9 +25,13 @@ to remote peer again before opening channel\. .SH RETURN VALUE -On success, returns confirmation that the channel establishment has been -canceled\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBcancelled\fR (string): A message indicating it was cancelled by RPC + +.RE On error the returned object will contain \fBcode\fR and \fBmessage\fR properties, with \fBcode\fR being one of the following: @@ -60,4 +64,4 @@ lightning-openchannel_\fBsigned\fR(7), lightning-openchannel_\fBabort\fR(7) Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:72ea44cf6efea20b369fb3ecfb61c8ede142800cbc8b427dbb9a26ec254452ce +\" SHA256STAMP:d9006c6b2519f1ae2009a683e3e8131af6e182929d8f0025e451155e4e7d6fe6 diff --git a/doc/lightning-fundchannel_cancel.7.md b/doc/lightning-fundchannel_cancel.7.md index 8c2da4d26e45..80f24c3794f5 100644 --- a/doc/lightning-fundchannel_cancel.7.md +++ b/doc/lightning-fundchannel_cancel.7.md @@ -25,8 +25,10 @@ to remote peer again before opening channel. RETURN VALUE ------------ -On success, returns confirmation that the channel establishment has been -canceled. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **cancelled** (string): A message indicating it was cancelled by RPC +[comment]: # (GENERATE-FROM-SCHEMA-END) On error the returned object will contain `code` and `message` properties, with `code` being one of the following: @@ -56,3 +58,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:f639e58991b8a9b58b181e9f4bd325fb24368af5cb76f94a94ca21675421829b) diff --git a/doc/lightning-fundchannel_complete.7 b/doc/lightning-fundchannel_complete.7 index 41a0acda82c3..566fabe0d92b 100644 --- a/doc/lightning-fundchannel_complete.7 +++ b/doc/lightning-fundchannel_complete.7 @@ -26,9 +26,15 @@ unrecoverable loss of funds\. .SH RETURN VALUE -On success, returns a confirmation that \fIcommitments_secured\fR and the -derived \fIchannel_id\fR\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBchannel_id\fR (hex): The channel_id of the resulting channel (always 64 characters) +.IP \[bu] +\fBcommitments_secured\fR (boolean): Indication that channel is safe to use (always \fItrue\fR) + +.RE On error the returned object will contain \fBcode\fR and \fBmessage\fR properties, with \fBcode\fR being one of the following: @@ -62,4 +68,4 @@ lightning-openchannel_\fBabort\fR(7) Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:13e5fee7b987b38e9b08685f4b49062314ec9e2cf52afbb5a2c5a4965fe2b01f +\" SHA256STAMP:72b7e8826beb42061bf53e59b72c35c4fba6cdb421e491ffe6bf07dc51412342 diff --git a/doc/lightning-fundchannel_complete.7.md b/doc/lightning-fundchannel_complete.7.md index 06b8b53b7d89..8a2a95957032 100644 --- a/doc/lightning-fundchannel_complete.7.md +++ b/doc/lightning-fundchannel_complete.7.md @@ -26,8 +26,11 @@ unrecoverable loss of funds. RETURN VALUE ------------ -On success, returns a confirmation that *commitments\_secured* and the -derived *channel\_id*. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **channel_id** (hex): The channel_id of the resulting channel (always 64 characters) +- **commitments_secured** (boolean): Indication that channel is safe to use (always *true*) +[comment]: # (GENERATE-FROM-SCHEMA-END) On error the returned object will contain `code` and `message` properties, with `code` being one of the following: @@ -57,3 +60,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:7cb52658d81c5834e34a6011c04283bf740fe6e5feedb0208778abad5fddc519) diff --git a/doc/lightning-fundchannel_start.7 b/doc/lightning-fundchannel_start.7 index 256e0e0e3c59..fa212251fff1 100644 --- a/doc/lightning-fundchannel_start.7 +++ b/doc/lightning-fundchannel_start.7 @@ -45,10 +45,17 @@ transaction before that can lead to unrecoverable loss of funds\. .SH RETURN VALUE -On success, returns the \fIfunding_address\fR and the \fIscriptpubkey\fR for the channel funding output\. -If a \fBclose_to\fR address was provided, will close to this address iff the \fBclose_to\fR address is -returned in the response\. Otherwise, the peer does not support \fBoption_upfront_shutdownscript\fR\. +On success, an object is returned, containing: +.RS +.IP \[bu] +\fBfunding_address\fR (string): The address to send funding to for the channel +.IP \[bu] +\fBscriptpubkey\fR (hex): The raw scriptPubkey for the address +.IP \[bu] +\fBclose_to\fR (hex, optional): The raw scriptPubkey which mutual close will go to; only present if \fIclose_to\fR parameter was specified and peer supports \fBoption_upfront_shutdown_script\fR + +.RE On error the returned object will contain \fBcode\fR and \fBmessage\fR properties, with \fBcode\fR being one of the following: @@ -86,4 +93,4 @@ lightning-openchannel_\fBabort\fR(7) Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:6dd30561d6b00b1f2d5595cfe199e09576eb4ba2afea7ba52e8f38546720c76c +\" SHA256STAMP:959e97d593fb77f9403e8c5024d5582a2ae81b5cffae1799a285e0a26281b770 diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index ff1d8b046149..5f34431b6d42 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -41,9 +41,12 @@ transaction before that can lead to unrecoverable loss of funds. RETURN VALUE ------------ -On success, returns the *funding\_address* and the *scriptpubkey* for the channel funding output. -If a `close_to` address was provided, will close to this address iff the `close_to` address is -returned in the response. Otherwise, the peer does not support `option_upfront_shutdownscript`. +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **funding_address** (string): The address to send funding to for the channel +- **scriptpubkey** (hex): The raw scriptPubkey for the address +- **close_to** (hex, optional): The raw scriptPubkey which mutual close will go to; only present if *close_to* parameter was specified and peer supports `option_upfront_shutdown_script` +[comment]: # (GENERATE-FROM-SCHEMA-END) On error the returned object will contain `code` and `message` properties, with `code` being one of the following: @@ -75,3 +78,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:9417a2d8d48b69f3557b08984d6b362e20cf20eeefcbef168fdc84b6f1de6411) diff --git a/doc/lightning-fundpsbt.7 b/doc/lightning-fundpsbt.7 index 160885b3e12d..b7c12cc51030 100644 --- a/doc/lightning-fundpsbt.7 +++ b/doc/lightning-fundpsbt.7 @@ -122,4 +122,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:6420ab94377f1a25df686e97e79da3c67c69f99978b6177432426dfd45296052 +\" SHA256STAMP:b02d5c31f0fe6b5423871f4f5859519b41a882388d17f460bd7331f714880126 diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index 9e15a668d047..ae5bde3896f1 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -110,3 +110,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:25b32367f5bbf6f1e53036c38b7b7d19eebf62fa749a3ab580e4093eedc88584) diff --git a/doc/lightning-getinfo.7 b/doc/lightning-getinfo.7 index 92bab929a7a1..402fc691badb 100644 --- a/doc/lightning-getinfo.7 +++ b/doc/lightning-getinfo.7 @@ -22,43 +22,69 @@ The \fBgetinfo\fR gives a summary of the current running node\. .fi .SH RETURN VALUE -On success, an object with the following information is returned: +On success, an object is returned, containing: .RS .IP \[bu] -\fIid\fR: A string that represents the public key of the node\. It will represents the node on the public network\. +\fBid\fR (pubkey): The public key unique to this node .IP \[bu] -\fIalias\fR: A string that represents the alias of the node, by default is calculate from the public key (node id)\. This is just for fun; the name can be anything and is not unique! +\fBalias\fR (string): The fun alias this node will advertize (up to 32 characters) .IP \[bu] -\fIcolor\fR: A string that represents the preferred color of the node\. +\fBcolor\fR (hex): The favorite RGB color this node will advertize (always 6 characters) .IP \[bu] -\fInum_peers\fR: An integer that represents the number of peer connect to the node\. +\fBnum_peers\fR (u32): The total count of peers, connected or with channels .IP \[bu] -\fInum_pending_channels\fR: An integer that represents the number of channel which are still awaiting opening confirmation\. +\fBnum_pending_channels\fR (u32): The total count of channels being opened .IP \[bu] -\fInum_active_channels\fR: A integer that represents the number of channel which are currently open\. +\fBnum_active_channels\fR (u32): The total count of channels in normal state .IP \[bu] -\fInum_inactive_channels\fR: A integer that represents the number of channel which are closing\. +\fBnum_inactive_channels\fR (u32): The total count of channels waiting for opening or closing transactions to be mined .IP \[bu] -\fIaddress\fR: An array that represents all published addresses of the node, each object inside the array contains the following proprieties: +\fBversion\fR (string): Identifies what bugs you are running into +.IP \[bu] +\fBlightning-dir\fR (string): Identifies where you can find the configuration and other related files +.IP \[bu] +\fBblockheight\fR (u32): The highest block height we've learned +.IP \[bu] +\fBnetwork\fR (string): represents the type of network on the node are working (e\.g: \fBbitcoin\fR, \fBtestnet\fR, or \fBregtest\fR) +.IP \[bu] +\fBfees_collected_msat\fR (msat): Total routing fees collected by this node +.IP \[bu] +\fBaddress\fR (array of objects, optional): The addresses we announce to the world: .RS .IP \[bu] -\fItype\fR: A string that represents the type of the address (currently \fBipv4\fR, \fBipv6\fR, \fBtorv3\fR or \fBtorv4\fR)\. +\fBtype\fR (string): Type of connection (one of "ipv4", "ipv6", "torv2", "torv3") .IP \[bu] -\fIaddress\fR: A string that represents the value of the address\. +\fBaddress\fR (string): address in expected format for \fBtype\fR .IP \[bu] -\fIport\fR: An integer that represents the port where the node is listening with this address\. +\fBport\fR (u16): port number .RE .IP \[bu] -\fIbinding\fR: An array that represents all addresses where the node is binded\. Each object contains the same object type of the address propriety above\. +\fBbinding\fR (array of objects, optional): The addresses we are listening on: +.RS +.IP \[bu] +\fBtype\fR (string): Type of connection (one of "local socket", "ipv4", "ipv6", "torv2", "torv3") +.IP \[bu] +\fBaddress\fR (string, optional): address in expected format for \fBtype\fR +.IP \[bu] +\fBport\fR (u16, optional): port number .IP \[bu] -\fIversion\fR: A string that represents the version of the node\. +\fBsocket\fR (string, optional): socket filename (only if \fBtype\fR is "local socket") + +.RE + + +.RE + +The following warnings may also be returned: + +.RS .IP \[bu] -\fIblockheight\fR: An integer that represents the blockchain height\. +\fBwarning_bitcoind_sync\fR: Bitcoind is not up-to-date with network\. .IP \[bu] -\fInetwork\fR: A string that represents the type of network on the node are working (e\.g: \fBbitcoin\fR, \fBtestnet\fR, or \fBregtest\fR)\. +\fBwarning_lightningd_sync\fR: Lightningd is still loading latest blocks from bitcoind\. .RE @@ -121,4 +147,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:9e6a0e0c25569bfc9d23b07e84392a3f70f6c94a86cd482fbf48c3d668a1796d +\" SHA256STAMP:637babe08b35ca524666bc51f1d0191e2006064af2b4c22997fbbe49cc7f935c diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index a5e09b2313a9..3419953eaac6 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -25,23 +25,34 @@ EXAMPLE JSON REQUEST RETURN VALUE ------------ -On success, an object with the following information is returned: - -- *id*: A string that represents the public key of the node. It will represents the node on the public network. -- *alias*: A string that represents the alias of the node, by default is calculate from the public key (node id). This is just for fun; the name can be anything and is not unique! -- *color*: A string that represents the preferred color of the node. -- *num_peers*: An integer that represents the number of peer connect to the node. -- *num_pending_channels*: An integer that represents the number of channel which are still awaiting opening confirmation. -- *num_active_channels*: A integer that represents the number of channel which are currently open. -- *num_inactive_channels*: A integer that represents the number of channel which are closing. -- *address*: An array that represents all published addresses of the node, each object inside the array contains the following proprieties: - - *type*: A string that represents the type of the address (currently `ipv4`, `ipv6`, `torv3` or `torv4`). - - *address*: A string that represents the value of the address. - - *port*: An integer that represents the port where the node is listening with this address. -- *binding*: An array that represents all addresses where the node is binded. Each object contains the same object type of the address propriety above. -- *version*: A string that represents the version of the node. -- *blockheight*: An integer that represents the blockchain height. -- *network*: A string that represents the type of network on the node are working (e.g: `bitcoin`, `testnet`, or `regtest`). +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **id** (pubkey): The public key unique to this node +- **alias** (string): The fun alias this node will advertize (up to 32 characters) +- **color** (hex): The favorite RGB color this node will advertize (always 6 characters) +- **num_peers** (u32): The total count of peers, connected or with channels +- **num_pending_channels** (u32): The total count of channels being opened +- **num_active_channels** (u32): The total count of channels in normal state +- **num_inactive_channels** (u32): The total count of channels waiting for opening or closing transactions to be mined +- **version** (string): Identifies what bugs you are running into +- **lightning-dir** (string): Identifies where you can find the configuration and other related files +- **blockheight** (u32): The highest block height we've learned +- **network** (string): represents the type of network on the node are working (e.g: `bitcoin`, `testnet`, or `regtest`) +- **fees_collected_msat** (msat): Total routing fees collected by this node +- **address** (array of objects, optional): The addresses we announce to the world: + - **type** (string): Type of connection (one of "ipv4", "ipv6", "torv2", "torv3") + - **address** (string): address in expected format for **type** + - **port** (u16): port number +- **binding** (array of objects, optional): The addresses we are listening on: + - **type** (string): Type of connection (one of "local socket", "ipv4", "ipv6", "torv2", "torv3") + - **address** (string, optional): address in expected format for **type** + - **port** (u16, optional): port number + - **socket** (string, optional): socket filename (only if **type** is "local socket") + +The following warnings may also be returned: +- **warning_bitcoind_sync**: Bitcoind is not up-to-date with network. +- **warning_lightningd_sync**: Lightningd is still loading latest blocks from bitcoind. +[comment]: # (GENERATE-FROM-SCHEMA-END) On failure, one of the following error codes may be returned: @@ -103,3 +114,4 @@ RESOURCES --------- Main web site: +[comment]: # ( SHA256STAMP:2aa0f0cf9de7b2d373bdce8b337535a0197ad8cb1df2cdb0e043ba49c3704816) diff --git a/doc/lightning-getlog.7 b/doc/lightning-getlog.7 index 13eedfb0471d..2fe16a77e5a2 100644 --- a/doc/lightning-getlog.7 +++ b/doc/lightning-getlog.7 @@ -29,31 +29,63 @@ The \fBgetlog\fR the RPC command to show logs, with optional log \fIlevel\fR\. .fi .SH RETURN VALUE -On success, a object will be return with the following parameters: +On success, an object is returned, containing: .RS .IP \[bu] -\fIcreated_at\fR: An floating point value that represents the UNIX timestamp when logging began\. +\fBcreated_at\fR (string): UNIX timestamp with 9 decimal places, when logging was initialized .IP \[bu] -\fIbytes_used\fR: A string that represents the dimension in bytes of the log file\. +\fBbytes_used\fR (u32): The number of bytes used by logging records .IP \[bu] -\fIbytes_max\fR: An integer that represents the max dimension in bytes of log file\. +\fBbytes_max\fR (u32): The bytes_used values at which records will be trimmed .IP \[bu] -\fIlog\fR: An array of objects where each element contains the following proprieties: + +\fBlog\fR (array of objects): + .RS .IP \[bu] -\fItype\fR: A string that represents the log level\. The propriety can have an value equal to SKIPPED to indicate the existence of omitted entries\. +\fBtype\fR (string) (one of "SKIPPED", "BROKEN", "UNUSUAL", "INFO", "DEBUG", "IO_IN", "IO_OUT") + +.RE + +If \fBtype\fR is "SKIPPED": + +.RS .IP \[bu] -\fItime\fR: A floating point value that represents the time since \fIcreated_at\fR\. +\fBnum_skipped\fR (u32): number of unprinted log entries (deleted or below \fIlevel\fR parameter) + +.RE + +If \fBtype\fR is "BROKEN", "UNUSUAL", "INFO" or "DEBUG": + +.RS .IP \[bu] -\fIsource\fR: A string that represents the source of line\. +\fBtime\fR (string): UNIX timestamp with 9 decimal places after \fBcreated_at\fR .IP \[bu] -\fIlog\fR: A string that represents the content of line\. +\fBsource\fR (string): The particular logbook this was found in +.IP \[bu] +\fBlog\fR (string): The actual log message +.IP \[bu] +\fBnode_id\fR (pubkey, optional): The peer this is associated with .RE +If \fBtype\fR is "IO_IN" or "IO_OUT": + +.RS +.IP \[bu] +\fBtime\fR (string): Seconds after \fBcreated_at\fR, with 9 decimal places +.IP \[bu] +\fBsource\fR (string): The particular logbook this was found in .IP \[bu] -\fInum_skipped\fR: An integer that it is present only if the log level is equal to SKIPPED\. +\fBlog\fR (string): The associated log message +.IP \[bu] +\fBdata\fR (hex): The IO which occurred +.IP \[bu] +\fBnode_id\fR (pubkey, optional): The peer this is associated with + +.RE + .RE @@ -95,4 +127,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:789e23927120d0fefd374592a3c655244fd6c28a122368bdd8da2f3cdde66798 +\" SHA256STAMP:13e3ab43fb6811bff8bee102ba85e7d4cb8eb97d11bb0720ae5c0c576ba021ec diff --git a/doc/lightning-getlog.7.md b/doc/lightning-getlog.7.md index dad81dc0fecb..4fffb2d8d1be 100644 --- a/doc/lightning-getlog.7.md +++ b/doc/lightning-getlog.7.md @@ -28,18 +28,30 @@ EXAMPLE JSON REQUEST RETURN VALUE ------------ -On success, a object will be return with the following parameters: - -- *created_at*: An floating point value that represents the UNIX timestamp when logging began. -- *bytes_used*: A string that represents the dimension in bytes of the log file. -- *bytes_max*: An integer that represents the max dimension in bytes of log file. -- *log*: An array of objects where each element contains the following proprieties: - - *type*: A string that represents the log level. The propriety can have an value equal to SKIPPED to indicate the existence of omitted entries. - - *time*: A floating point value that represents the time since *created_at*. - - *source*: A string that represents the source of line. - - *log*: A string that represents the content of line. -- *num_skipped*: An integer that it is present only if the log level is equal to SKIPPED. - +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **created_at** (string): UNIX timestamp with 9 decimal places, when logging was initialized +- **bytes_used** (u32): The number of bytes used by logging records +- **bytes_max** (u32): The bytes_used values at which records will be trimmed +- **log** (array of objects): + - **type** (string) (one of "SKIPPED", "BROKEN", "UNUSUAL", "INFO", "DEBUG", "IO_IN", "IO_OUT") + + If **type** is "SKIPPED": + - **num_skipped** (u32): number of unprinted log entries (deleted or below *level* parameter) + + If **type** is "BROKEN", "UNUSUAL", "INFO" or "DEBUG": + - **time** (string): UNIX timestamp with 9 decimal places after **created_at** + - **source** (string): The particular logbook this was found in + - **log** (string): The actual log message + - **node_id** (pubkey, optional): The peer this is associated with + + If **type** is "IO_IN" or "IO_OUT": + - **time** (string): Seconds after **created_at**, with 9 decimal places + - **source** (string): The particular logbook this was found in + - **log** (string): The associated log message + - **data** (hex): The IO which occurred + - **node_id** (pubkey, optional): The peer this is associated with +[comment]: # (GENERATE-FROM-SCHEMA-END) On failure, one of the following error codes may be returned: @@ -77,3 +89,4 @@ RESOURCES --------- Main web site: +[comment]: # ( SHA256STAMP:db99eeb155bb44ebda8b77afdc1fad773e82fa8892e1df6afd61c60a1f4b7ec3) diff --git a/doc/lightning-getroute.7 b/doc/lightning-getroute.7 index 70f2675d9162..3d2d9c88caf4 100644 --- a/doc/lightning-getroute.7 +++ b/doc/lightning-getroute.7 @@ -111,17 +111,29 @@ factor for larger amounts, and is basically ignored for tiny ones\. .SH RETURN VALUE -On success, a "route" array is returned\. Each array element contains -\fIid\fR (the node being routed through), \fImsatoshi\fR (the millisatoshis -sent), \fIamount_msat\fR (the same, with \fImsat\fR appended), \fIdelay\fR (the -number of blocks to timeout at this node), and \fIstyle\fR (indicating -the features which can be used for this hop)\. +On success, an object containing \fBroute\fR is returned\. It is an array of objects, where each object contains: +.RS +.IP \[bu] +\fBid\fR (pubkey): The node at the end of this hop +.IP \[bu] +\fBchannel\fR (short_channel_id): The channel joining these nodes +.IP \[bu] +\fBdirection\fR (u32): 0 if this channel is traversed from lesser to greater \fBid\fR, otherwise 1 +.IP \[bu] +\fBamount_msat\fR (msat): The amount expected by the node at the end of this hop +.IP \[bu] +\fBdelay\fR (u32): The total CLTV expected by the node at the end of this hop +.IP \[bu] +\fBstyle\fR (string): The features understood by the destination node (one of "legacy", "tlv") + +.RE The final \fIid\fR will be the destination \fIid\fR given in the input\. The difference between the first \fImsatoshi\fR minus the \fImsatoshi\fR given in -the input is the fee\. The first \fIdelay\fR is the very worst case timeout -for the payment failure, in blocks\. +the input is the fee (assuming the first hop is free)\. The first +\fIdelay\fR is the very worst case timeout for the payment failure, in +blocks\. .SH AUTHOR @@ -135,4 +147,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:52fe58e923c36f62601e6cb2779031d0428d3561bdeae2ab8eadf3dff84a3179 +\" SHA256STAMP:aaf1dab77352de52f8bf8a5c3e4a6449769b5dbfa51bdbf9b1a06575c8ae37fe diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index 435e3142c84e..1c84d58fc819 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -277,16 +277,21 @@ factor for larger amounts, and is basically ignored for tiny ones. RETURN VALUE ------------ -On success, a "route" array is returned. Each array element contains -*id* (the node being routed through), *msatoshi* (the millisatoshis -sent), *amount\_msat* (the same, with *msat* appended), *delay* (the -number of blocks to timeout at this node), and *style* (indicating -the features which can be used for this hop). +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **route** is returned. It is an array of objects, where each object contains: +- **id** (pubkey): The node at the end of this hop +- **channel** (short_channel_id): The channel joining these nodes +- **direction** (u32): 0 if this channel is traversed from lesser to greater **id**, otherwise 1 +- **amount_msat** (msat): The amount expected by the node at the end of this hop +- **delay** (u32): The total CLTV expected by the node at the end of this hop +- **style** (string): The features understood by the destination node (one of "legacy", "tlv") +[comment]: # (GENERATE-FROM-SCHEMA-END) The final *id* will be the destination *id* given in the input. The difference between the first *msatoshi* minus the *msatoshi* given in -the input is the fee. The first *delay* is the very worst case timeout -for the payment failure, in blocks. +the input is the fee (assuming the first hop is free). The first +*delay* is the very worst case timeout for the payment failure, in +blocks. AUTHOR ------ @@ -303,3 +308,4 @@ RESOURCES Main web site: +[comment]: # ( SHA256STAMP:c15c56751270e8a3df25f3e3f72fbe8ea56366e5fe1157a8485b85cec1878982) diff --git a/doc/schemas/disableoffer.schema.json b/doc/schemas/disableoffer.schema.json new file mode 100644 index 000000000000..6b745c8b7b7a --- /dev/null +++ b/doc/schemas/disableoffer.schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ "offer_id", "active", "single_use", "bolt12", "used" ], + "additionalProperties": false, + "properties": { + "offer_id": { + "type": "hex", + "description": "the merkle hash of the offer", + "maxLength": 64, + "minLength": 64 + }, + "active": { + "type": "boolean", + "enum": [ "false" ], + "description": "Whether the offer can produce invoices/payments" + }, + "single_use": { + "type": "boolean", + "description": "Whether the offer is disabled after first successful use" + }, + "bolt12": { + "type": "string", + "description": "The bolt12 string representing this offer" + }, + "used": { + "type": "boolean", + "description": "Whether the offer has had an invoice paid / payment made" + }, + "label": { + "type": "string", + "description": "The label provided when offer was created" + } + } +} diff --git a/doc/schemas/disconnect.schema.json b/doc/schemas/disconnect.schema.json new file mode 100644 index 000000000000..b797a82c2f6f --- /dev/null +++ b/doc/schemas/disconnect.schema.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": { + } +} diff --git a/doc/schemas/feerates.schema.json b/doc/schemas/feerates.schema.json new file mode 100644 index 000000000000..3f25817e7303 --- /dev/null +++ b/doc/schemas/feerates.schema.json @@ -0,0 +1,119 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ ], + "properties": { + "warning_missing_feerates": { + "type": "string", + "description": "Some fee estimates are missing" + }, + "perkb": { + "type": "object", + "description": "If *style* parameter was perkb", + "additionalProperties": false, + "required": [ "min_acceptable", "max_acceptable" ], + "properties": { + "min_acceptable": { + "type": "u32", + "description": "The smallest feerate that you can use, usually the minimum relayed feerate of the backend" + }, + "max_acceptable": { + "type": "u32", + "description": "The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet)." + }, + "opening": { + "type": "u32", + "description": "Default feerate for lightning-fundchannel(7) and lightning-withdraw(7)" + }, + "mutual_close": { + "type": "u32", + "description": "Feerate to aim for in cooperative shutdown. Note that since mutual close is a **negotiation**, the actual feerate used in mutual close will be somewhere between this and the corresponding mutual close feerate of the peer." + }, + "unilateral_close": { + "type": "u32", + "description": "Feerate for commitment_transaction in a live channel which we originally funded" + }, + "delayed_to_us": { + "type": "u32", + "description": "Feerate for returning unilateral close funds to our wallet" + }, + "htlc_resolution": { + "type": "u32", + "description": "Feerate for returning unilateral close HTLC outputs to our wallet" + }, + "penalty": { + "type": "u32", + "description": "Feerate to start at when penalizing a cheat attempt" + } + } + }, + "perkw": { + "type": "object", + "description": "If *style* parameter was perkw", + "additionalProperties": false, + "required": [ "min_acceptable", "max_acceptable" ], + "properties": { + "min_acceptable": { + "type": "u32", + "description": "The smallest feerate that you can use, usually the minimum relayed feerate of the backend" + }, + "max_acceptable": { + "type": "u32", + "description": "The largest feerate we will accept from remote negotiations. If a peer attempts to set the feerate higher than this we will unilaterally close the channel (or simply forget it if it's not open yet)." + }, + "opening": { + "type": "u32", + "description": "Default feerate for lightning-fundchannel(7) and lightning-withdraw(7)" + }, + "mutual_close": { + "type": "u32", + "description": "Feerate to aim for in cooperative shutdown. Note that since mutual close is a **negotiation**, the actual feerate used in mutual close will be somewhere between this and the corresponding mutual close feerate of the peer." + }, + "unilateral_close": { + "type": "u32", + "description": "Feerate for commitment_transaction in a live channel which we originally funded" + }, + "delayed_to_us": { + "type": "u32", + "description": "Feerate for returning unilateral close funds to our wallet" + }, + "htlc_resolution": { + "type": "u32", + "description": "Feerate for returning unilateral close HTLC outputs to our wallet" + }, + "penalty": { + "type": "u32", + "description": "Feerate to start at when penalizing a cheat attempt" + } + } + }, + "onchain_fee_estimates": { + "type": "object", + "additionalProperties": false, + "required": [ "opening_channel_satoshis", "mutual_close_satoshis", "unilateral_close_satoshis", "htlc_timeout_satoshis", "htlc_success_satoshis" ], + "properties": { + "opening_channel_satoshis": { + "type": "u64", + "description": "Estimated cost of typical channel open" + }, + "mutual_close_satoshis": { + "type": "u64", + "description": "Estimated cost of typical channel close" + }, + "unilateral_close_satoshis": { + "type": "u64", + "description": "Estimated cost of typical unilateral close (without HTLCs)" + }, + "htlc_timeout_satoshis": { + "type": "u64", + "description": "Estimated cost of typical HTLC timeout transaction" + }, + "htlc_success_satoshis": { + "type": "u64", + "description": "Estimated cost of typical HTLC fulfillment transaction" + } + } + } + } +} diff --git a/doc/schemas/fetchinvoice.schema.json b/doc/schemas/fetchinvoice.schema.json new file mode 100644 index 000000000000..ff538b58cfda --- /dev/null +++ b/doc/schemas/fetchinvoice.schema.json @@ -0,0 +1,68 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "invoice", "changes" ], + "properties": { + "invoice": { + "type": "string", + "description": "The BOLT12 invoice we fetched" + }, + "changes": { + "type": "object", + "description": "Summary of changes from offer", + "additionalProperties": false, + "required": [ ], + "properties": { + "description_appended": { + "type": "string", + "description": "extra characters appended to the *description* field." + }, + "description": { + "type": "string", + "description": "a completely replaced *description* field" + }, + "vendor_removed": { + "type": "string", + "description": "The *vendor* from the offer, which is missing in the invoice" + }, + "vendor": { + "type": "string", + "description": "a completely replaced *vendor* field" + }, + "msat": { + "type": "msat", + "description": "the amount, if different from the offer amount multiplied by any *quantity* (or the offer had no amount, or was not in BTC)." + } + } + }, + "next_period": { + "type": "object", + "description": "Only for recurring invoices if the next period is under the *recurrence_limit*", + "additionalProperties": false, + "required": [ "counter", "starttime", "endtime", "paywindow_start", "paywindow_end" ], + "properties": { + "counter": { + "type": "u64", + "description": "the index of the next period to fetchinvoice" + }, + "starttime": { + "type": "u64", + "description": "UNIX timestamp that the next period starts" + }, + "endtime": { + "type": "u64", + "description": "UNIX timestamp that the next period ends" + }, + "paywindow_start": { + "type": "u64", + "description": "UNIX timestamp of the earliest time that the next invoice can be fetched" + }, + "paywindow_end": { + "type": "u64", + "description": "UNIX timestamp of the latest time that the next invoice can be fetched" + } + } + } + } +} diff --git a/doc/schemas/fundchannel.schema.json b/doc/schemas/fundchannel.schema.json new file mode 100644 index 000000000000..44775d377513 --- /dev/null +++ b/doc/schemas/fundchannel.schema.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "tx", "txid", "outnum", "channel_id" ], + "properties": { + "tx": { + "type": "hex", + "description": "The raw transaction which funded the channel" + }, + "txid": { + "type": "txid", + "description": "The txid of the transaction which funded the channel" + }, + "outnum": { + "type": "u32", + "description": "The 0-based output index showing which output funded the channel" + }, + "channel_id": { + "type": "hex", + "description": "The channel_id of the resulting channel", + "minLength": 64, + "maxLength": 64 + }, + "close_to": { + "type": "hex", + "description": "The raw scriptPubkey which mutual close will go to; only present if *close_to* parameter was specified and peer supports `option_upfront_shutdown_script`" + } + } +} diff --git a/doc/schemas/fundchannel_cancel.schema.json b/doc/schemas/fundchannel_cancel.schema.json new file mode 100644 index 000000000000..a9e1e663e091 --- /dev/null +++ b/doc/schemas/fundchannel_cancel.schema.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "cancelled" ], + "properties": { + "cancelled": { + "type": "string", + "description": "A message indicating it was cancelled by RPC" + } + } +} diff --git a/doc/schemas/fundchannel_complete.schema.json b/doc/schemas/fundchannel_complete.schema.json new file mode 100644 index 000000000000..90b29faeefc0 --- /dev/null +++ b/doc/schemas/fundchannel_complete.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "channel_id", "commitments_secured" ], + "properties": { + "channel_id": { + "type": "hex", + "description": "The channel_id of the resulting channel", + "minLength": 64, + "maxLength": 64 + }, + "commitments_secured": { + "type": "boolean", + "enum": [ true ], + "description": "Indication that channel is safe to use" + } + } +} diff --git a/doc/schemas/fundchannel_start.schema.json b/doc/schemas/fundchannel_start.schema.json new file mode 100644 index 000000000000..335e1eb1f0b4 --- /dev/null +++ b/doc/schemas/fundchannel_start.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "funding_address", "scriptpubkey" ], + "properties": { + "funding_address": { + "type": "string", + "description": "The address to send funding to for the channel" + }, + "scriptpubkey": { + "type": "hex", + "description": "The raw scriptPubkey for the address" + }, + "close_to": { + "type": "hex", + "description": "The raw scriptPubkey which mutual close will go to; only present if *close_to* parameter was specified and peer supports `option_upfront_shutdown_script`" + } + } +} diff --git a/doc/schemas/fundpsbt.schema.json b/doc/schemas/fundpsbt.schema.json new file mode 100644 index 000000000000..c935136224c0 --- /dev/null +++ b/doc/schemas/fundpsbt.schema.json @@ -0,0 +1,61 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "psbt", "feerate_per_kw", "estimated_final_weight", "excess_msat" ], + "properties": { + "psbt": { + "type": "string", + "description": "Unsigned PSBT which fulfills the parameters given" + }, + "feerate_per_kw": { + "type": "u32", + "description": "The feerate used to create the PSBT, in satoshis-per-kiloweight" + }, + "estimated_final_weight": { + "type": "u32", + "description": "The estimated weight of the transaction once fully signed" + }, + "excess_msat": { + "type": "msat", + "description": "The amount above *satoshi* which is available. This could be zero, or dust; it will be zero if *change_outnum* is also returned" + }, + "change_outnum": { + "type": "u32", + "description": "The 0-based output number where change was placed (only if parameter *excess_as_change* was true and there was sufficient funds)" + }, + "reservations": { + "type": "array", + "description": "If the *reserve* was true, just as per lightning-reserveinputs(7)", + "items": { + "type": "object", + "required": ["txid", "vout", "was_reserved", "reserved", "reserved_to_block" ], + "additionalProperties": false, + "properties": { + "txid": { + "type": "txid", + "description": "The txid of the transaction" + }, + "vout": { + "type": "u32", + "description": "The 0-based output number" + }, + "was_reserved": { + "type": "boolean", + "enum": [ false ], + "description": "Whether this output was previously reserved" + }, + "reserved": { + "type": "boolean", + "enum": [ true ], + "description": "Whether this output is now reserved" + }, + "reserved_to_block": { + "type": "u32", + "description": "The blockheight the reservation will expire" + } + } + } + } + } +} diff --git a/doc/schemas/getinfo.schema.json b/doc/schemas/getinfo.schema.json new file mode 100644 index 000000000000..3c964fee25f9 --- /dev/null +++ b/doc/schemas/getinfo.schema.json @@ -0,0 +1,124 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "id", "alias", "color", "num_peers", "num_pending_channels", "num_active_channels", "num_inactive_channels", "version", "blockheight", "network", "fees_collected_msat", "lightning-dir" ], + "properties": { + "id": { + "type": "pubkey", + "description": "The public key unique to this node" + }, + "alias": { + "type": "string", + "description": "The fun alias this node will advertize", + "maxLength": 32 + }, + "color": { + "type": "hex", + "description": "The favorite RGB color this node will advertize", + "minLength": 6, + "maxLength": 6 + }, + "num_peers": { + "type": "u32", + "description": "The total count of peers, connected or with channels" + }, + "num_pending_channels": { + "type": "u32", + "description": "The total count of channels being opened" + }, + "num_active_channels": { + "type": "u32", + "description": "The total count of channels in normal state" + }, + "num_inactive_channels": { + "type": "u32", + "description": "The total count of channels waiting for opening or closing transactions to be mined" + }, + "version": { + "type": "string", + "description": "Identifies what bugs you are running into" + }, + "lightning-dir": { + "type": "string", + "description": "Identifies where you can find the configuration and other related files" + }, + "blockheight": { + "type": "u32", + "description": "The highest block height we've learned" + }, + "network": { + "type": "string", + "description": "represents the type of network on the node are working (e.g: `bitcoin`, `testnet`, or `regtest`)" + }, + "msatoshi_fees_collected": { + "type": "u64", + "deprecated": true + }, + "fees_collected_msat": { + "type": "msat", + "description": "Total routing fees collected by this node" + }, + "address": { + "type": "array", + "description": "The addresses we announce to the world", + "items": { + "type": "object", + "required": [ "type", "address", "port" ], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ "ipv4", "ipv6", "torv2", "torv3" ], + "description": "Type of connection" + }, + "address": { + "type": "string", + "description": "address in expected format for **type**" + }, + "port": { + "type": "u16", + "description": "port number" + } + } + } + }, + "binding": { + "type": "array", + "description": "The addresses we are listening on", + "items": { + "type": "object", + "required": [ "type" ], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "*FIXME*": "The variant in connect.schema.json is more complete", + "enum": [ "local socket", "ipv4", "ipv6", "torv2", "torv3" ], + "description": "Type of connection" + }, + "address": { + "type": "string", + "description": "address in expected format for **type**" + }, + "port": { + "type": "u16", + "description": "port number" + }, + "socket": { + "type": "string", + "description": "socket filename (only if **type** is \"local socket\")" + } + } + } + }, + "warning_bitcoind_sync": { + "type": "string", + "description": "Bitcoind is not up-to-date with network." + }, + "warning_lightningd_sync": { + "type": "string", + "description": "Lightningd is still loading latest blocks from bitcoind." + } + } +} diff --git a/doc/schemas/getlog.schema.json b/doc/schemas/getlog.schema.json new file mode 100644 index 000000000000..23c9981b5b69 --- /dev/null +++ b/doc/schemas/getlog.schema.json @@ -0,0 +1,127 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "created_at", "bytes_used", "bytes_max", "log" ], + "properties": { + "created_at": { + "type": "string", + "description": "UNIX timestamp with 9 decimal places, when logging was initialized" + }, + "bytes_used": { + "type": "u32", + "description": "The number of bytes used by logging records" + }, + "bytes_max": { + "type": "u32", + "description": "The bytes_used values at which records will be trimmed " + }, + "log": { + "type": "array", + "items": { + "type": "object", + "required": [ "type" ], + "additionalProperties": true, + "properties": { + "type": { + "type": "string", + "enum": [ "SKIPPED", "BROKEN", "UNUSUAL", "INFO", "DEBUG", "IO_IN", "IO_OUT" ] + } + }, + "allOf": [ + { + "if": { + "additionalProperties": true, + "properties": { + "type": { + "enum": [ "SKIPPED" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "num_skipped" ], + "properties": { + "type": { }, + "num_skipped": { + "type": "u32", + "description": "number of unprinted log entries (deleted or below *level* parameter)" + } + } + } + }, + { + "if": { + "additionalProperties": true, + "properties": { + "type": { + "enum": [ "BROKEN", "UNUSUAL", "INFO", "DEBUG" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "time", "source", "log" ], + "properties": { + "type": { }, + "time": { + "type": "string", + "description": "UNIX timestamp with 9 decimal places after **created_at**" + }, + "source": { + "type": "string", + "description": "The particular logbook this was found in" + }, + "log": { + "type": "string", + "description": "The actual log message" + }, + "node_id": { + "type": "pubkey", + "description": "The peer this is associated with" + } + } + } + }, + { + "if": { + "additionalProperties": true, + "properties": { + "type": { + "enum": [ "IO_IN", "IO_OUT" ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ "time", "source", "log", "data" ], + "properties": { + "type": { }, + "time": { + "type": "string", + "description": "Seconds after **created_at**, with 9 decimal places" + }, + "source": { + "type": "string", + "description": "The particular logbook this was found in" + }, + "log": { + "type": "string", + "description": "The associated log message" + }, + "node_id": { + "type": "pubkey", + "description": "The peer this is associated with" + }, + "data": { + "type": "hex", + "description": "The IO which occurred" + } + } + } + } + ] + } + } + } +} diff --git a/doc/schemas/getroute.schema.json b/doc/schemas/getroute.schema.json new file mode 100644 index 000000000000..9e5ebe7ebc03 --- /dev/null +++ b/doc/schemas/getroute.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ "route" ], + "properties": { + "route": { + "type": "array", + "items": { + "type": "object", + "required": [ "id", "direction", "channel", "amount_msat", "delay", "style" ], + "additionalProperties": false, + "properties": { + "id": { + "type": "pubkey", + "description": "The node at the end of this hop" + }, + "channel": { + "type": "short_channel_id", + "description": "The channel joining these nodes" + }, + "direction": { + "type": "u32", + "description": "0 if this channel is traversed from lesser to greater **id**, otherwise 1" + }, + "msatoshi": { + "type": "u64", + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "The amount expected by the node at the end of this hop" + }, + "delay": { + "type": "u32", + "description": "The total CLTV expected by the node at the end of this hop" + }, + "style": { + "type": "string", + "description": "The features understood by the destination node", + "enum": [ "legacy", "tlv" ] + } + } + } + } + } +} diff --git a/tests/test_misc.py b/tests/test_misc.py index e3ebd56858c6..c1dee2b55546 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2546,3 +2546,16 @@ def test_notimestamp_logging(node_factory): assert l1.daemon.logs[0].startswith("DEBUG") assert l1.rpc.listconfigs()['log-timestamps'] is False + + +def test_getlog(node_factory): + """Test the getlog command""" + l1 = node_factory.get_node(options={'log-level': 'io'}) + + # Default will skip some entries + logs = l1.rpc.getlog()['log'] + assert [l for l in logs if l['type'] == 'SKIPPED'] != [] + + # This should not + logs = l1.rpc.getlog(level='io')['log'] + assert [l for l in logs if l['type'] == 'SKIPPED'] == [] From a027b3a80cad8cd72c8629d44d687c48d9bfb181 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 27 May 2021 13:55:58 +0930 Subject: [PATCH 19/62] plugins/test/Makefile: fix typo causing build race. Signed-off-by: Rusty Russell --- plugins/test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/test/Makefile b/plugins/test/Makefile index a65ae8b11ea8..67591ecc5ed3 100644 --- a/plugins/test/Makefile +++ b/plugins/test/Makefile @@ -4,7 +4,7 @@ PLUGIN_TEST_SRC := $(wildcard plugins/test/run-*.c) PLUGIN_TEST_OBJS := $(PLUGIN_TEST_SRC:.c=.o) PLUGIN_TEST_PROGRAMS := $(PLUGIN_TEST_OBJS:.o=) -ALL_C_SOURCES += $(PLUGIN__TEST_SRC) +ALL_C_SOURCES += $(PLUGIN_TEST_SRC) ALL_TEST_PROGRAMS += $(PLUGIN_TEST_PROGRAMS) PLUGIN_TEST_COMMON_OBJS := \ From 57935b20d6a81a0335261d0ded350aef41a0f2d3 Mon Sep 17 00:00:00 2001 From: Nalin Bhardwaj Date: Sat, 29 May 2021 14:25:13 +0530 Subject: [PATCH 20/62] lightningd: check closing tx signature Changelog-None --- lightningd/closing_control.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 2df07d6f5de8..a4769cdf81df 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -97,15 +97,27 @@ static void peer_received_closing_signature(struct channel *channel, struct bitcoin_tx *tx; struct bitcoin_txid tx_id; struct lightningd *ld = channel->peer->ld; + u8 *funding_wscript; if (!fromwire_closingd_received_signature(msg, msg, &sig, &tx)) { - channel_internal_error(channel, "Bad closing_received_signature %s", + channel_internal_error(channel, + "Bad closing_received_signature %s", tal_hex(msg, msg)); return; } tx->chainparams = chainparams; - /* FIXME: Make sure signature is correct! */ + funding_wscript = bitcoin_redeem_2of2(tmpctx, + &channel->local_funding_pubkey, + &channel->channel_info.remote_fundingkey); + if (!check_tx_sig(tx, 0, NULL, funding_wscript, + &channel->channel_info.remote_fundingkey, &sig)) { + channel_internal_error(channel, + "Bad closing_received_signature %s", + tal_hex(msg, msg)); + return; + } + if (closing_fee_is_acceptable(ld, channel, tx)) { channel_set_last_tx(channel, tx, &sig, TX_CHANNEL_CLOSE); wallet_channel_save(ld->wallet, channel); From 431683293f55a144704b50fe67b43a74d676af00 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 May 2021 11:55:02 +0200 Subject: [PATCH 21/62] libhsmd: Correctly wrap with `tmpctx` management in libhsmd-python --- contrib/libhsmd_python/libhsmd_python.c | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/contrib/libhsmd_python/libhsmd_python.c b/contrib/libhsmd_python/libhsmd_python.c index fb199152d41b..3186b7ae3487 100644 --- a/contrib/libhsmd_python/libhsmd_python.c +++ b/contrib/libhsmd_python/libhsmd_python.c @@ -1,11 +1,12 @@ #include #include +#include char *init(char *hex_hsm_secret, char *network_name) { const struct bip32_key_version *key_version; struct secret sec; u8 *response; - setup_locale(); + common_setup(NULL); if (sodium_init() == -1) { fprintf( stderr, @@ -45,30 +46,29 @@ char *init(char *hex_hsm_secret, char *network_name) { } char *handle(long long cap, long long dbid, char *peer_id, char *hexmsg) { - const tal_t *ctx = tal_arr(NULL, u8, 0); size_t res_len; - u8 *response, *request = tal_hexdata(ctx, hexmsg, strlen(hexmsg)); + u8 *response, *request; char *res; struct hsmd_client *client; struct node_id *peer = NULL; - printf("%llu: %s\n", cap, hexmsg); + request = tal_hexdata(tmpctx, hexmsg, strlen(hexmsg)); if (peer_id != NULL) { - peer = tal(ctx, struct node_id); + peer = tal(tmpctx, struct node_id); node_id_from_hexstr(hexmsg, strlen(hexmsg), peer); - client = hsmd_client_new_peer(ctx, cap, dbid, peer, NULL); + client = hsmd_client_new_peer(tmpctx, cap, dbid, peer, NULL); } else { - client = hsmd_client_new_main(ctx, cap, NULL); + client = hsmd_client_new_main(tmpctx, cap, NULL); + } + response = hsmd_handle_client_message(tmpctx, client, request); + if (response == NULL) { + clean_tmpctx(); + return NULL; } - response = hsmd_handle_client_message(NULL, client, request); - printf("%s\n", tal_hex(ctx, response)); - if (response == NULL) - return tal_free(ctx); - res = tal_hex(NULL, response); res_len = hex_str_size(tal_bytelen(response)); res = malloc(res_len); hex_encode(response, tal_bytelen(response), res, res_len); - tal_free(ctx); + clean_tmpctx(); return res; } From c6a3d30bec5e70d0357c0e1d63549d4d6cf6f243 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 May 2021 14:37:30 +0200 Subject: [PATCH 22/62] libhsmd: Always use gcc to configure and compile --- contrib/libhsmd_python/setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contrib/libhsmd_python/setup.py b/contrib/libhsmd_python/setup.py index be24b24cd6a5..c965f46079a8 100644 --- a/contrib/libhsmd_python/setup.py +++ b/contrib/libhsmd_python/setup.py @@ -19,7 +19,7 @@ def __init__(self, name, **kwargs): # The directory we compile external depencies is architecture specific. external_target = pathlib.Path("external") / subprocess.check_output( - ["clang", "-dumpmachine"] + ["gcc", "-dumpmachine"] ).strip().decode("ASCII") @@ -46,7 +46,12 @@ def build_make(self, ext): cwd=cwd, ) - subprocess.check_call(["./configure"], cwd=cwd / "src") + subprocess.check_call([ + "./configure", + "--disable-developer", + "--disable-valgrind", + "CC=gcc" + ], cwd=cwd / "src") # Selectively build some targets we rely on later subprocess.check_call(["make", "lightningd/lightning_hsmd"], cwd=srcdir) From e27ce7d5b6a3f273a620e11cacf119b1b2057c1a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 May 2021 14:44:49 +0200 Subject: [PATCH 23/62] libhsmd: Add a simple tx signature test --- contrib/libhsmd_python/test_libhsmd.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 contrib/libhsmd_python/test_libhsmd.py diff --git a/contrib/libhsmd_python/test_libhsmd.py b/contrib/libhsmd_python/test_libhsmd.py new file mode 100644 index 000000000000..47104162fdf3 --- /dev/null +++ b/contrib/libhsmd_python/test_libhsmd.py @@ -0,0 +1,14 @@ +from libhsmd import init as libhsmd_init, handle as libhsmd_handle + + +def test_fundchannel(): + secret = '9f5a9ba98d7e816eebf496db2ff760dc17a4a2f0ae5a87c37cab4bbf6ee05530' + network = 'testnet' + msg = '00130200000001f25c0c5f21c46ed3f1063a9a41a489ed4e6bb2c18ef1998eb6618198b17137f90000000000666a6c8001e985010000000000160014f4d3100ee3828a602cbc47b1d70ac204e3342081f06a98200000012d70736274ff0100520200000001f25c0c5f21c46ed3f1063a9a41a489ed4e6bb2c18ef1998eb6618198b17137f90000000000666a6c8001e985010000000000160014f4d3100ee3828a602cbc47b1d70ac204e3342081f06a98200001012ba086010000000000220020cc2ef6e3d8102826a2167b463a77bfaf5b57c1ec24c52115d3c471320ad475720105475221026ecfe4d4dd089bbadcf19c860580dae91b2752261269c1f770f32f80e80680492102df1c73d6d45af5edac96953e0eaae5677411b46791092b4bef2c59648b83c20c52ae220602df1c73d6d45af5edac96953e0eaae5677411b46791092b4bef2c59648b83c20c0841c77c75000000002206026ecfe4d4dd089bbadcf19c860580dae91b2752261269c1f770f32f80e806804908109206e600000000000002df1c73d6d45af5edac96953e0eaae5677411b46791092b4bef2c59648b83c20c039c7fa80e43780ad63af6df575924b4c9ae3f1ad22f74067c28227fb45817b2e301' + + res = libhsmd_init(secret, network) + capabilities = 24 + dbid = 1 + node_id = "02312627fdf07fbdd7e5ddb136611bdde9b00d26821d14d94891395452f67af248" + res = libhsmd_handle(capabilities, dbid, node_id, msg) + assert(res.startswith('0070')) From 90ced7ecde678dbe7cbdc17f2a4df2822ab4e62e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 18 May 2021 14:55:57 +0200 Subject: [PATCH 24/62] libhsmd: Do not use the message as the peer ID... --- contrib/libhsmd_python/libhsmd_python.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/libhsmd_python/libhsmd_python.c b/contrib/libhsmd_python/libhsmd_python.c index 3186b7ae3487..ebdef4c55d99 100644 --- a/contrib/libhsmd_python/libhsmd_python.c +++ b/contrib/libhsmd_python/libhsmd_python.c @@ -54,7 +54,7 @@ char *handle(long long cap, long long dbid, char *peer_id, char *hexmsg) { request = tal_hexdata(tmpctx, hexmsg, strlen(hexmsg)); if (peer_id != NULL) { peer = tal(tmpctx, struct node_id); - node_id_from_hexstr(hexmsg, strlen(hexmsg), peer); + node_id_from_hexstr(peer_id, strlen(peer_id), peer); client = hsmd_client_new_peer(tmpctx, cap, dbid, peer, NULL); } else { client = hsmd_client_new_main(tmpctx, cap, NULL); From 0ed7c0d083bbd375e347a586b7dee9b8887ba16e Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 26 May 2021 21:05:09 +0200 Subject: [PATCH 25/62] Suggested code cleanup by TODO comment This commit introduces the code cleanup suggested by the TODO comment in the code. Basically, it moves the code from the if-else statement to a switch statement without the default case. I used the basic idea of the code used in PR #4507. Changelog-Changed: None. Signed-off-by: Vincenzo Palazzo --- wallet/db_postgres_sqlgen.c | 2 +- wallet/db_sqlite3_sqlgen.c | 2 +- wallet/statements_gettextgen.po | 244 ++++++++++++++++---------------- wallet/wallet.c | 51 +++++-- 4 files changed, 166 insertions(+), 133 deletions(-) diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index ad7731c67973..c178b7b5e008 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:387f4000b85417999e9b27c6d795d4f19012a5515e2464312e865b7f654d70dd +// SHA256STAMP:3ab5cc5d3610b26e4df12de155aa85990ac825c0e75a1adf410e67e2ca173327 diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 555d957577e5..85e3c8e3344f 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:387f4000b85417999e9b27c6d795d4f19012a5515e2464312e865b7f654d70dd +// SHA256STAMP:3ab5cc5d3610b26e4df12de155aa85990ac825c0e75a1adf410e67e2ca173327 diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index affe6b033276..574487c12eba 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -758,487 +758,487 @@ msgstr "" msgid "SELECT state, payment_key, payment_hash, label, msatoshi, expiry_time, pay_index, msatoshi_received, paid_timestamp, bolt11, description, features, local_offer_id FROM invoices WHERE id = ?;" msgstr "" -#: wallet/wallet.c:47 +#: wallet/wallet.c:65 msgid "SELECT txid, outnum FROM utxoset WHERE spendheight is NULL" msgstr "" -#: wallet/wallet.c:88 wallet/wallet.c:570 +#: wallet/wallet.c:106 wallet/wallet.c:588 msgid "SELECT * from outputs WHERE prev_out_tx=? AND prev_out_index=?" msgstr "" -#: wallet/wallet.c:102 wallet/wallet.c:584 +#: wallet/wallet.c:120 wallet/wallet.c:602 msgid "INSERT INTO outputs ( prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:219 +#: wallet/wallet.c:237 msgid "UPDATE outputs SET status=? WHERE status=? AND prev_out_tx=? AND prev_out_index=?" msgstr "" -#: wallet/wallet.c:227 +#: wallet/wallet.c:245 msgid "UPDATE outputs SET status=? WHERE prev_out_tx=? AND prev_out_index=?" msgstr "" -#: wallet/wallet.c:246 +#: wallet/wallet.c:264 msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs" msgstr "" -#: wallet/wallet.c:263 +#: wallet/wallet.c:281 msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status= ? " msgstr "" -#: wallet/wallet.c:301 +#: wallet/wallet.c:319 msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL" msgstr "" -#: wallet/wallet.c:338 +#: wallet/wallet.c:356 msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?" msgstr "" -#: wallet/wallet.c:430 +#: wallet/wallet.c:448 msgid "UPDATE outputs SET status=?, reserved_til=? WHERE prev_out_tx=? AND prev_out_index=?" msgstr "" -#: wallet/wallet.c:519 +#: wallet/wallet.c:537 msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();" msgstr "" -#: wallet/wallet.c:688 +#: wallet/wallet.c:706 msgid "INSERT INTO shachains (min_index, num_valid) VALUES (?, 0);" msgstr "" -#: wallet/wallet.c:732 +#: wallet/wallet.c:750 msgid "UPDATE shachains SET num_valid=?, min_index=? WHERE id=?" msgstr "" -#: wallet/wallet.c:739 +#: wallet/wallet.c:757 msgid "UPDATE shachain_known SET idx=?, hash=? WHERE shachain_id=? AND pos=?" msgstr "" -#: wallet/wallet.c:751 +#: wallet/wallet.c:769 msgid "INSERT INTO shachain_known (shachain_id, pos, idx, hash) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:773 +#: wallet/wallet.c:791 msgid "SELECT min_index, num_valid FROM shachains WHERE id=?" msgstr "" -#: wallet/wallet.c:788 +#: wallet/wallet.c:806 msgid "SELECT idx, hash, pos FROM shachain_known WHERE shachain_id=?" msgstr "" -#: wallet/wallet.c:811 +#: wallet/wallet.c:829 msgid "SELECT id, node_id, address FROM peers WHERE id=?;" msgstr "" -#: wallet/wallet.c:844 +#: wallet/wallet.c:862 msgid "SELECT signature FROM htlc_sigs WHERE channelid = ?" msgstr "" -#: wallet/wallet.c:878 +#: wallet/wallet.c:896 msgid "SELECT remote_ann_node_sig, remote_ann_bitcoin_sig FROM channels WHERE id = ?" msgstr "" -#: wallet/wallet.c:922 +#: wallet/wallet.c:940 msgid "SELECT hstate, feerate_per_kw FROM channel_feerates WHERE channel_id = ?" msgstr "" -#: wallet/wallet.c:955 +#: wallet/wallet.c:973 msgid "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:990 +#: wallet/wallet.c:1008 msgid "UPDATE channel_funding_inflights SET funding_psbt=?, funding_tx_remote_sigs_received=? WHERE channel_id=? AND funding_tx_id=? AND funding_tx_outnum=?" msgstr "" -#: wallet/wallet.c:1013 +#: wallet/wallet.c:1031 msgid "DELETE FROM channel_funding_inflights WHERE channel_id = ?" msgstr "" -#: wallet/wallet.c:1061 +#: wallet/wallet.c:1079 msgid "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate" msgstr "" -#: wallet/wallet.c:1290 +#: wallet/wallet.c:1308 msgid "SELECT id FROM channels ORDER BY id DESC LIMIT 1;" msgstr "" -#: wallet/wallet.c:1307 +#: wallet/wallet.c:1325 msgid "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, option_static_remotekey, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum FROM channels WHERE state != ?;" msgstr "" -#: wallet/wallet.c:1405 +#: wallet/wallet.c:1432 msgid "UPDATE channels SET in_payments_offered = COALESCE(in_payments_offered, 0) + 1 , in_msatoshi_offered = COALESCE(in_msatoshi_offered, 0) + ? WHERE id = ?;" msgstr "" -#: wallet/wallet.c:1410 +#: wallet/wallet.c:1438 msgid "UPDATE channels SET in_payments_fulfilled = COALESCE(in_payments_fulfilled, 0) + 1 , in_msatoshi_fulfilled = COALESCE(in_msatoshi_fulfilled, 0) + ? WHERE id = ?;" msgstr "" -#: wallet/wallet.c:1415 +#: wallet/wallet.c:1444 msgid "UPDATE channels SET out_payments_offered = COALESCE(out_payments_offered, 0) + 1 , out_msatoshi_offered = COALESCE(out_msatoshi_offered, 0) + ? WHERE id = ?;" msgstr "" -#: wallet/wallet.c:1420 +#: wallet/wallet.c:1450 msgid "UPDATE channels SET out_payments_fulfilled = COALESCE(out_payments_fulfilled, 0) + 1 , out_msatoshi_fulfilled = COALESCE(out_msatoshi_fulfilled, 0) + ? WHERE id = ?;" msgstr "" -#: wallet/wallet.c:1462 +#: wallet/wallet.c:1495 msgid "SELECT in_payments_offered, in_payments_fulfilled, in_msatoshi_offered, in_msatoshi_fulfilled, out_payments_offered, out_payments_fulfilled, out_msatoshi_offered, out_msatoshi_fulfilled FROM channels WHERE id = ?" msgstr "" -#: wallet/wallet.c:1491 +#: wallet/wallet.c:1524 msgid "SELECT MIN(height), MAX(height) FROM blocks;" msgstr "" -#: wallet/wallet.c:1513 +#: wallet/wallet.c:1546 msgid "INSERT INTO channel_configs DEFAULT VALUES;" msgstr "" -#: wallet/wallet.c:1525 +#: wallet/wallet.c:1558 msgid "UPDATE channel_configs SET dust_limit_satoshis=?, max_htlc_value_in_flight_msat=?, channel_reserve_satoshis=?, htlc_minimum_msat=?, to_self_delay=?, max_accepted_htlcs=? WHERE id=?;" msgstr "" -#: wallet/wallet.c:1549 +#: wallet/wallet.c:1582 msgid "SELECT id, dust_limit_satoshis, max_htlc_value_in_flight_msat, channel_reserve_satoshis, htlc_minimum_msat, to_self_delay, max_accepted_htlcs FROM channel_configs WHERE id= ? ;" msgstr "" -#: wallet/wallet.c:1583 +#: wallet/wallet.c:1616 msgid "UPDATE channels SET remote_ann_node_sig=?, remote_ann_bitcoin_sig=? WHERE id=?" msgstr "" -#: wallet/wallet.c:1602 +#: wallet/wallet.c:1635 msgid "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, option_static_remotekey=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=? WHERE id=?" msgstr "" -#: wallet/wallet.c:1694 +#: wallet/wallet.c:1727 msgid "UPDATE channels SET fundingkey_remote=?, revocation_basepoint_remote=?, payment_basepoint_remote=?, htlc_basepoint_remote=?, delayed_payment_basepoint_remote=?, per_commit_remote=?, old_per_commit_remote=?, channel_config_remote=?, future_per_commitment_point=? WHERE id=?" msgstr "" -#: wallet/wallet.c:1721 +#: wallet/wallet.c:1754 msgid "DELETE FROM channel_feerates WHERE channel_id=?" msgstr "" -#: wallet/wallet.c:1731 +#: wallet/wallet.c:1764 msgid "INSERT INTO channel_feerates VALUES(?, ?, ?)" msgstr "" -#: wallet/wallet.c:1748 +#: wallet/wallet.c:1781 msgid "UPDATE channels SET last_sent_commit=? WHERE id=?" msgstr "" -#: wallet/wallet.c:1771 +#: wallet/wallet.c:1804 msgid "INSERT INTO channel_state_changes ( channel_id, timestamp, old_state, new_state, cause, message) VALUES (?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:1799 +#: wallet/wallet.c:1832 msgid "SELECT timestamp, old_state, new_state, cause, message FROM channel_state_changes WHERE channel_id = ? ORDER BY timestamp ASC;" msgstr "" -#: wallet/wallet.c:1828 +#: wallet/wallet.c:1861 msgid "SELECT id FROM peers WHERE node_id = ?" msgstr "" -#: wallet/wallet.c:1840 +#: wallet/wallet.c:1873 msgid "UPDATE peers SET address = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:1849 +#: wallet/wallet.c:1882 msgid "INSERT INTO peers (node_id, address) VALUES (?, ?);" msgstr "" -#: wallet/wallet.c:1870 +#: wallet/wallet.c:1903 msgid "INSERT INTO channels ( peer_id, first_blocknum, id, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local) VALUES (?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:1911 +#: wallet/wallet.c:1944 msgid "DELETE FROM channel_htlcs WHERE channel_id=?" msgstr "" -#: wallet/wallet.c:1917 +#: wallet/wallet.c:1950 msgid "DELETE FROM htlc_sigs WHERE channelid=?" msgstr "" -#: wallet/wallet.c:1923 +#: wallet/wallet.c:1956 msgid "DELETE FROM channeltxs WHERE channel_id=?" msgstr "" -#: wallet/wallet.c:1930 +#: wallet/wallet.c:1963 msgid "DELETE FROM channel_funding_inflights WHERE channel_id=?" msgstr "" -#: wallet/wallet.c:1936 +#: wallet/wallet.c:1969 msgid "DELETE FROM shachains WHERE id IN ( SELECT shachain_remote_id FROM channels WHERE channels.id=?)" msgstr "" -#: wallet/wallet.c:1946 +#: wallet/wallet.c:1979 msgid "UPDATE channels SET state=?, peer_id=? WHERE channels.id=?" msgstr "" -#: wallet/wallet.c:1960 +#: wallet/wallet.c:1993 msgid "SELECT * FROM channels WHERE peer_id = ?;" msgstr "" -#: wallet/wallet.c:1968 +#: wallet/wallet.c:2001 msgid "DELETE FROM peers WHERE id=?" msgstr "" -#: wallet/wallet.c:1979 +#: wallet/wallet.c:2012 msgid "UPDATE outputs SET confirmation_height = ? WHERE prev_out_tx = ?" msgstr "" -#: wallet/wallet.c:2082 +#: wallet/wallet.c:2115 msgid "INSERT INTO channel_htlcs ( channel_id, channel_htlc_id, direction, msatoshi, cltv_expiry, payment_hash, payment_key, hstate, shared_secret, routing_onion, received_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:2135 +#: wallet/wallet.c:2168 msgid "INSERT INTO channel_htlcs ( channel_id, channel_htlc_id, direction, origin_htlc, msatoshi, cltv_expiry, payment_hash, payment_key, hstate, routing_onion, malformed_onion, partid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?);" msgstr "" -#: wallet/wallet.c:2196 +#: wallet/wallet.c:2229 msgid "UPDATE channel_htlcs SET hstate=?, payment_key=?, malformed_onion=?, failuremsg=?, localfailmsg=?, we_filled=? WHERE id=?" msgstr "" -#: wallet/wallet.c:2412 +#: wallet/wallet.c:2445 msgid "SELECT id, channel_htlc_id, msatoshi, cltv_expiry, hstate, payment_hash, payment_key, routing_onion, failuremsg, malformed_onion, origin_htlc, shared_secret, received_time, we_filled FROM channel_htlcs WHERE direction= ? AND channel_id= ? AND hstate != ?" msgstr "" -#: wallet/wallet.c:2459 +#: wallet/wallet.c:2492 msgid "SELECT id, channel_htlc_id, msatoshi, cltv_expiry, hstate, payment_hash, payment_key, routing_onion, failuremsg, malformed_onion, origin_htlc, shared_secret, received_time, partid, localfailmsg FROM channel_htlcs WHERE direction = ? AND channel_id = ? AND hstate != ?" msgstr "" -#: wallet/wallet.c:2590 +#: wallet/wallet.c:2623 msgid "SELECT channel_id, direction, cltv_expiry, channel_htlc_id, payment_hash FROM channel_htlcs WHERE channel_id = ?;" msgstr "" -#: wallet/wallet.c:2624 +#: wallet/wallet.c:2657 msgid "DELETE FROM channel_htlcs WHERE direction = ? AND origin_htlc = ? AND payment_hash = ? AND partid = ?;" msgstr "" -#: wallet/wallet.c:2677 +#: wallet/wallet.c:2710 msgid "SELECT status FROM payments WHERE payment_hash=? AND partid = ?;" msgstr "" -#: wallet/wallet.c:2695 +#: wallet/wallet.c:2728 msgid "INSERT INTO payments ( status, payment_hash, destination, msatoshi, timestamp, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, total_msat, partid, local_offer_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:2784 +#: wallet/wallet.c:2817 msgid "DELETE FROM payments WHERE payment_hash = ? AND partid = ?" msgstr "" -#: wallet/wallet.c:2798 +#: wallet/wallet.c:2831 msgid "DELETE FROM payments WHERE payment_hash = ?" msgstr "" -#: wallet/wallet.c:2899 +#: wallet/wallet.c:2932 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ? AND partid = ?" msgstr "" -#: wallet/wallet.c:2949 +#: wallet/wallet.c:2982 msgid "UPDATE payments SET status=? WHERE payment_hash=? AND partid=?" msgstr "" -#: wallet/wallet.c:2959 +#: wallet/wallet.c:2992 msgid "UPDATE payments SET payment_preimage=? WHERE payment_hash=? AND partid=?" msgstr "" -#: wallet/wallet.c:2969 +#: wallet/wallet.c:3002 msgid "UPDATE payments SET path_secrets = NULL , route_nodes = NULL , route_channels = NULL WHERE payment_hash = ? AND partid = ?;" msgstr "" -#: wallet/wallet.c:3001 +#: wallet/wallet.c:3034 msgid "SELECT failonionreply, faildestperm, failindex, failcode, failnode, failchannel, failupdate, faildetail, faildirection FROM payments WHERE payment_hash=? AND partid=?;" msgstr "" -#: wallet/wallet.c:3068 +#: wallet/wallet.c:3101 msgid "UPDATE payments SET failonionreply=? , faildestperm=? , failindex=? , failcode=? , failnode=? , failchannel=? , failupdate=? , faildetail=? , faildirection=? WHERE payment_hash=? AND partid=?;" msgstr "" -#: wallet/wallet.c:3127 +#: wallet/wallet.c:3160 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ?;" msgstr "" -#: wallet/wallet.c:3149 +#: wallet/wallet.c:3182 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments ORDER BY id;" msgstr "" -#: wallet/wallet.c:3200 +#: wallet/wallet.c:3233 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE local_offer_id = ?;" msgstr "" -#: wallet/wallet.c:3245 +#: wallet/wallet.c:3278 msgid "DELETE FROM htlc_sigs WHERE channelid = ?" msgstr "" -#: wallet/wallet.c:3252 +#: wallet/wallet.c:3285 msgid "INSERT INTO htlc_sigs (channelid, signature) VALUES (?, ?)" msgstr "" -#: wallet/wallet.c:3264 +#: wallet/wallet.c:3297 msgid "SELECT blobval FROM vars WHERE name='genesis_hash'" msgstr "" -#: wallet/wallet.c:3288 +#: wallet/wallet.c:3321 msgid "INSERT INTO vars (name, blobval) VALUES ('genesis_hash', ?);" msgstr "" -#: wallet/wallet.c:3306 +#: wallet/wallet.c:3339 msgid "SELECT txid, outnum FROM utxoset WHERE spendheight < ?" msgstr "" -#: wallet/wallet.c:3318 +#: wallet/wallet.c:3351 msgid "DELETE FROM utxoset WHERE spendheight < ?" msgstr "" -#: wallet/wallet.c:3326 wallet/wallet.c:3440 +#: wallet/wallet.c:3359 wallet/wallet.c:3473 msgid "INSERT INTO blocks (height, hash, prev_hash) VALUES (?, ?, ?);" msgstr "" -#: wallet/wallet.c:3345 +#: wallet/wallet.c:3378 msgid "DELETE FROM blocks WHERE hash = ?" msgstr "" -#: wallet/wallet.c:3351 +#: wallet/wallet.c:3384 msgid "SELECT * FROM blocks WHERE height >= ?;" msgstr "" -#: wallet/wallet.c:3360 +#: wallet/wallet.c:3393 msgid "DELETE FROM blocks WHERE height > ?" msgstr "" -#: wallet/wallet.c:3372 +#: wallet/wallet.c:3405 msgid "UPDATE outputs SET spend_height = ?, status = ? WHERE prev_out_tx = ? AND prev_out_index = ?" msgstr "" -#: wallet/wallet.c:3390 +#: wallet/wallet.c:3423 msgid "UPDATE utxoset SET spendheight = ? WHERE txid = ? AND outnum = ?" msgstr "" -#: wallet/wallet.c:3413 wallet/wallet.c:3451 +#: wallet/wallet.c:3446 wallet/wallet.c:3484 msgid "INSERT INTO utxoset ( txid, outnum, blockheight, spendheight, txindex, scriptpubkey, satoshis) VALUES(?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3477 +#: wallet/wallet.c:3510 msgid "SELECT height FROM blocks WHERE height = ?" msgstr "" -#: wallet/wallet.c:3490 +#: wallet/wallet.c:3523 msgid "SELECT txid, spendheight, scriptpubkey, satoshis FROM utxoset WHERE blockheight = ? AND txindex = ? AND outnum = ? AND spendheight IS NULL" msgstr "" -#: wallet/wallet.c:3532 +#: wallet/wallet.c:3565 msgid "SELECT blockheight, txindex, outnum FROM utxoset WHERE spendheight = ?" msgstr "" -#: wallet/wallet.c:3563 wallet/wallet.c:3723 +#: wallet/wallet.c:3596 wallet/wallet.c:3756 msgid "SELECT blockheight FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3573 +#: wallet/wallet.c:3606 msgid "INSERT INTO transactions ( id, blockheight, txindex, rawtx) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3594 +#: wallet/wallet.c:3627 msgid "UPDATE transactions SET blockheight = ?, txindex = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:3611 +#: wallet/wallet.c:3644 msgid "INSERT INTO transaction_annotations (txid, idx, location, type, channel) VALUES (?, ?, ?, ?, ?) ON CONFLICT(txid,idx) DO NOTHING;" msgstr "" -#: wallet/wallet.c:3643 +#: wallet/wallet.c:3676 msgid "SELECT type, channel_id FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3659 +#: wallet/wallet.c:3692 msgid "UPDATE transactions SET type = ?, channel_id = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:3678 +#: wallet/wallet.c:3711 msgid "SELECT type FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3701 +#: wallet/wallet.c:3734 msgid "SELECT rawtx FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3747 +#: wallet/wallet.c:3780 msgid "SELECT blockheight, txindex FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3775 +#: wallet/wallet.c:3808 msgid "SELECT id FROM transactions WHERE blockheight=?" msgstr "" -#: wallet/wallet.c:3794 +#: wallet/wallet.c:3827 msgid "INSERT INTO channeltxs ( channel_id, type, transaction_id, input_num, blockheight) VALUES (?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3818 +#: wallet/wallet.c:3851 msgid "SELECT DISTINCT(channel_id) FROM channeltxs WHERE type = ?;" msgstr "" -#: wallet/wallet.c:3839 +#: wallet/wallet.c:3872 msgid "SELECT c.type, c.blockheight, t.rawtx, c.input_num, c.blockheight - t.blockheight + 1 AS depth, t.id as txid FROM channeltxs c JOIN transactions t ON t.id = c.transaction_id WHERE c.channel_id = ? ORDER BY c.id ASC;" msgstr "" -#: wallet/wallet.c:3884 +#: wallet/wallet.c:3917 msgid "UPDATE forwarded_payments SET in_msatoshi=?, out_msatoshi=?, state=?, resolved_time=?, failcode=? WHERE in_htlc_id=?" msgstr "" -#: wallet/wallet.c:3942 +#: wallet/wallet.c:3975 msgid "INSERT INTO forwarded_payments ( in_htlc_id, out_htlc_id, in_channel_scid, out_channel_scid, in_msatoshi, out_msatoshi, state, received_time, resolved_time, failcode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4001 +#: wallet/wallet.c:4034 msgid "SELECT CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)FROM forwarded_payments WHERE state = ?;" msgstr "" -#: wallet/wallet.c:4050 +#: wallet/wallet.c:4083 msgid "SELECT f.state, in_msatoshi, out_msatoshi, hin.payment_hash as payment_hash, in_channel_scid, out_channel_scid, f.received_time, f.resolved_time, f.failcode FROM forwarded_payments f LEFT JOIN channel_htlcs hin ON (f.in_htlc_id = hin.id) WHERE (1 = ? OR f.state = ?) AND (1 = ? OR f.in_channel_scid = ?) AND (1 = ? OR f.out_channel_scid = ?)" msgstr "" -#: wallet/wallet.c:4172 +#: wallet/wallet.c:4205 msgid "SELECT t.id, t.rawtx, t.blockheight, t.txindex, t.type as txtype, c2.short_channel_id as txchan, a.location, a.idx as ann_idx, a.type as annotation_type, c.short_channel_id FROM transactions t LEFT JOIN transaction_annotations a ON (a.txid = t.id) LEFT JOIN channels c ON (a.channel = c.id) LEFT JOIN channels c2 ON (t.channel_id = c2.id) ORDER BY t.blockheight, t.txindex ASC" msgstr "" -#: wallet/wallet.c:4266 +#: wallet/wallet.c:4299 msgid "INSERT INTO penalty_bases ( channel_id, commitnum, txid, outnum, amount) VALUES (?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4291 +#: wallet/wallet.c:4324 msgid "SELECT commitnum, txid, outnum, amount FROM penalty_bases WHERE channel_id = ?" msgstr "" -#: wallet/wallet.c:4315 +#: wallet/wallet.c:4348 msgid "DELETE FROM penalty_bases WHERE channel_id = ? AND commitnum = ?" msgstr "" -#: wallet/wallet.c:4333 +#: wallet/wallet.c:4366 msgid "SELECT 1 FROM offers WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4346 +#: wallet/wallet.c:4379 msgid "INSERT INTO offers ( offer_id, bolt12, label, status) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4373 +#: wallet/wallet.c:4406 msgid "SELECT bolt12, label, status FROM offers WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4401 +#: wallet/wallet.c:4434 msgid "SELECT offer_id FROM offers;" msgstr "" -#: wallet/wallet.c:4427 +#: wallet/wallet.c:4460 msgid "UPDATE offers SET status=? WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4438 +#: wallet/wallet.c:4471 msgid "UPDATE invoices SET state=? WHERE state=? AND local_offer_id = ?;" msgstr "" -#: wallet/wallet.c:4466 +#: wallet/wallet.c:4499 msgid "SELECT status FROM offers WHERE offer_id = ?;" msgstr "" @@ -1257,4 +1257,4 @@ msgstr "" #: wallet/test/run-wallet.c:1647 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:a6f2ee44515575973c58448f5859f557e748752253c9111de107991374fe5539 +# SHA256STAMP:5d466ed1c95975f0155bdb67e995c14b7927634e810d319c1ba616327c37d80a diff --git a/wallet/wallet.c b/wallet/wallet.c index a4b7fc2cd0fd..7f53e7d35cfd 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -28,6 +28,24 @@ * to prune? */ #define UTXO_PRUNE_DEPTH 144 +/* 12 hours is usually enough reservation time */ +#define RESERVATION_INC (6 * 12) + +/* Possible channel state */ +enum channel_state_bucket { + IN_OFFERED = 0, + IN_FULLFILLED = 1, + OUT_OFFERED = 2, + OUT_FULLFILLED = 3, +}; + +/* channel state identifier */ +struct channel_state_param { + const char *dir_key; + const char *type_key; + const enum channel_state_bucket state; +}; + static void outpointfilters_init(struct wallet *w) { struct db_stmt *stmt; @@ -1389,6 +1407,15 @@ bool wallet_init_channels(struct wallet *w) return wallet_channels_load_active(w); } +static enum channel_state_bucket get_state_channel_db(const char *dir, const char *typ) +{ + enum channel_state_bucket channel_state = IN_OFFERED; + if (streq(dir, "out")) + channel_state += 2; + if (streq(typ, "fulfilled")) + channel_state += 1; + return channel_state; +} static void wallet_channel_stats_incr_x(struct wallet *w, @@ -1398,33 +1425,39 @@ void wallet_channel_stats_incr_x(struct wallet *w, struct amount_msat msat) { struct db_stmt *stmt; - const char *query; - /* TODO These would be much better as a switch statement, leaving - * these here for now in order to keep the commit clean. */ - if (streq(dir, "in") && streq(typ, "offered")) { + const char *query = NULL; + + switch (get_state_channel_db(dir, typ)) { + case IN_OFFERED: query = SQL("UPDATE channels" " SET in_payments_offered = COALESCE(in_payments_offered, 0) + 1" " , in_msatoshi_offered = COALESCE(in_msatoshi_offered, 0) + ?" " WHERE id = ?;"); - } else if (streq(dir, "in") && streq(typ, "fulfilled")) { + break; + case IN_FULLFILLED: query = SQL("UPDATE channels" " SET in_payments_fulfilled = COALESCE(in_payments_fulfilled, 0) + 1" " , in_msatoshi_fulfilled = COALESCE(in_msatoshi_fulfilled, 0) + ?" " WHERE id = ?;"); - } else if (streq(dir, "out") && streq(typ, "offered")) { + break; + case OUT_OFFERED: query = SQL("UPDATE channels" " SET out_payments_offered = COALESCE(out_payments_offered, 0) + 1" " , out_msatoshi_offered = COALESCE(out_msatoshi_offered, 0) + ?" " WHERE id = ?;"); - } else if (streq(dir, "out") && streq(typ, "fulfilled")) { + break; + case OUT_FULLFILLED: query = SQL("UPDATE channels" " SET out_payments_fulfilled = COALESCE(out_payments_fulfilled, 0) + 1" " , out_msatoshi_fulfilled = COALESCE(out_msatoshi_fulfilled, 0) + ?" " WHERE id = ?;"); - } else { - fatal("Unknown stats key %s %s", dir, typ); + break; } + // Sanity check! + if (!query) + fatal("Unknown channel state key (direction %s, type %s)", dir, typ); + stmt = db_prepare_v2(w->db, query); db_bind_amount_msat(stmt, 0, &msat); db_bind_u64(stmt, 1, cdbid); From a88a6e604cf01c26d22b7540c746e0ae05dfeb66 Mon Sep 17 00:00:00 2001 From: Nalin Bhardwaj Date: Thu, 27 May 2021 03:25:50 +0530 Subject: [PATCH 26/62] wallet: sort listsendpays by ID Changelog-Changed: JSON: `listsendpays` output is now ordered by `id`. --- wallet/db_postgres_sqlgen.c | 6 +-- wallet/db_sqlite3_sqlgen.c | 6 +-- wallet/statements_gettextgen.po | 96 ++++++++++++++++----------------- wallet/wallet.c | 3 +- 4 files changed, 56 insertions(+), 55 deletions(-) diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index c178b7b5e008..9525c7814b4f 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1593,8 +1593,8 @@ struct db_query db_postgres_queries[] = { .readonly = false, }, { - .name = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ?;", - .query = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = $1;", + .name = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ? ORDER BY id;", + .query = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = $1 ORDER BY id;", .placeholders = 1, .readonly = true, }, @@ -1906,4 +1906,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:3ab5cc5d3610b26e4df12de155aa85990ac825c0e75a1adf410e67e2ca173327 +// SHA256STAMP:435d8c98449934c86167d11929b515312babce55bae5487dc3cdc201cb4ba0fe diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 85e3c8e3344f..f6121dad17e7 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1593,8 +1593,8 @@ struct db_query db_sqlite3_queries[] = { .readonly = false, }, { - .name = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ?;", - .query = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ?;", + .name = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ? ORDER BY id;", + .query = "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ? ORDER BY id;", .placeholders = 1, .readonly = true, }, @@ -1906,4 +1906,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:3ab5cc5d3610b26e4df12de155aa85990ac825c0e75a1adf410e67e2ca173327 +// SHA256STAMP:435d8c98449934c86167d11929b515312babce55bae5487dc3cdc201cb4ba0fe diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index 574487c12eba..afd5883f41c4 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1055,190 +1055,190 @@ msgid "UPDATE payments SET failonionreply=? , faildestperm=? , failind msgstr "" #: wallet/wallet.c:3160 -msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ?;" +msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ? ORDER BY id;" msgstr "" -#: wallet/wallet.c:3182 +#: wallet/wallet.c:3183 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments ORDER BY id;" msgstr "" -#: wallet/wallet.c:3233 +#: wallet/wallet.c:3234 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE local_offer_id = ?;" msgstr "" -#: wallet/wallet.c:3278 +#: wallet/wallet.c:3279 msgid "DELETE FROM htlc_sigs WHERE channelid = ?" msgstr "" -#: wallet/wallet.c:3285 +#: wallet/wallet.c:3286 msgid "INSERT INTO htlc_sigs (channelid, signature) VALUES (?, ?)" msgstr "" -#: wallet/wallet.c:3297 +#: wallet/wallet.c:3298 msgid "SELECT blobval FROM vars WHERE name='genesis_hash'" msgstr "" -#: wallet/wallet.c:3321 +#: wallet/wallet.c:3322 msgid "INSERT INTO vars (name, blobval) VALUES ('genesis_hash', ?);" msgstr "" -#: wallet/wallet.c:3339 +#: wallet/wallet.c:3340 msgid "SELECT txid, outnum FROM utxoset WHERE spendheight < ?" msgstr "" -#: wallet/wallet.c:3351 +#: wallet/wallet.c:3352 msgid "DELETE FROM utxoset WHERE spendheight < ?" msgstr "" -#: wallet/wallet.c:3359 wallet/wallet.c:3473 +#: wallet/wallet.c:3360 wallet/wallet.c:3474 msgid "INSERT INTO blocks (height, hash, prev_hash) VALUES (?, ?, ?);" msgstr "" -#: wallet/wallet.c:3378 +#: wallet/wallet.c:3379 msgid "DELETE FROM blocks WHERE hash = ?" msgstr "" -#: wallet/wallet.c:3384 +#: wallet/wallet.c:3385 msgid "SELECT * FROM blocks WHERE height >= ?;" msgstr "" -#: wallet/wallet.c:3393 +#: wallet/wallet.c:3394 msgid "DELETE FROM blocks WHERE height > ?" msgstr "" -#: wallet/wallet.c:3405 +#: wallet/wallet.c:3406 msgid "UPDATE outputs SET spend_height = ?, status = ? WHERE prev_out_tx = ? AND prev_out_index = ?" msgstr "" -#: wallet/wallet.c:3423 +#: wallet/wallet.c:3424 msgid "UPDATE utxoset SET spendheight = ? WHERE txid = ? AND outnum = ?" msgstr "" -#: wallet/wallet.c:3446 wallet/wallet.c:3484 +#: wallet/wallet.c:3447 wallet/wallet.c:3485 msgid "INSERT INTO utxoset ( txid, outnum, blockheight, spendheight, txindex, scriptpubkey, satoshis) VALUES(?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3510 +#: wallet/wallet.c:3511 msgid "SELECT height FROM blocks WHERE height = ?" msgstr "" -#: wallet/wallet.c:3523 +#: wallet/wallet.c:3524 msgid "SELECT txid, spendheight, scriptpubkey, satoshis FROM utxoset WHERE blockheight = ? AND txindex = ? AND outnum = ? AND spendheight IS NULL" msgstr "" -#: wallet/wallet.c:3565 +#: wallet/wallet.c:3566 msgid "SELECT blockheight, txindex, outnum FROM utxoset WHERE spendheight = ?" msgstr "" -#: wallet/wallet.c:3596 wallet/wallet.c:3756 +#: wallet/wallet.c:3597 wallet/wallet.c:3757 msgid "SELECT blockheight FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3606 +#: wallet/wallet.c:3607 msgid "INSERT INTO transactions ( id, blockheight, txindex, rawtx) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3627 +#: wallet/wallet.c:3628 msgid "UPDATE transactions SET blockheight = ?, txindex = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:3644 +#: wallet/wallet.c:3645 msgid "INSERT INTO transaction_annotations (txid, idx, location, type, channel) VALUES (?, ?, ?, ?, ?) ON CONFLICT(txid,idx) DO NOTHING;" msgstr "" -#: wallet/wallet.c:3676 +#: wallet/wallet.c:3677 msgid "SELECT type, channel_id FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3692 +#: wallet/wallet.c:3693 msgid "UPDATE transactions SET type = ?, channel_id = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:3711 +#: wallet/wallet.c:3712 msgid "SELECT type FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3734 +#: wallet/wallet.c:3735 msgid "SELECT rawtx FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3780 +#: wallet/wallet.c:3781 msgid "SELECT blockheight, txindex FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3808 +#: wallet/wallet.c:3809 msgid "SELECT id FROM transactions WHERE blockheight=?" msgstr "" -#: wallet/wallet.c:3827 +#: wallet/wallet.c:3828 msgid "INSERT INTO channeltxs ( channel_id, type, transaction_id, input_num, blockheight) VALUES (?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3851 +#: wallet/wallet.c:3852 msgid "SELECT DISTINCT(channel_id) FROM channeltxs WHERE type = ?;" msgstr "" -#: wallet/wallet.c:3872 +#: wallet/wallet.c:3873 msgid "SELECT c.type, c.blockheight, t.rawtx, c.input_num, c.blockheight - t.blockheight + 1 AS depth, t.id as txid FROM channeltxs c JOIN transactions t ON t.id = c.transaction_id WHERE c.channel_id = ? ORDER BY c.id ASC;" msgstr "" -#: wallet/wallet.c:3917 +#: wallet/wallet.c:3918 msgid "UPDATE forwarded_payments SET in_msatoshi=?, out_msatoshi=?, state=?, resolved_time=?, failcode=? WHERE in_htlc_id=?" msgstr "" -#: wallet/wallet.c:3975 +#: wallet/wallet.c:3976 msgid "INSERT INTO forwarded_payments ( in_htlc_id, out_htlc_id, in_channel_scid, out_channel_scid, in_msatoshi, out_msatoshi, state, received_time, resolved_time, failcode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4034 +#: wallet/wallet.c:4035 msgid "SELECT CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)FROM forwarded_payments WHERE state = ?;" msgstr "" -#: wallet/wallet.c:4083 +#: wallet/wallet.c:4084 msgid "SELECT f.state, in_msatoshi, out_msatoshi, hin.payment_hash as payment_hash, in_channel_scid, out_channel_scid, f.received_time, f.resolved_time, f.failcode FROM forwarded_payments f LEFT JOIN channel_htlcs hin ON (f.in_htlc_id = hin.id) WHERE (1 = ? OR f.state = ?) AND (1 = ? OR f.in_channel_scid = ?) AND (1 = ? OR f.out_channel_scid = ?)" msgstr "" -#: wallet/wallet.c:4205 +#: wallet/wallet.c:4206 msgid "SELECT t.id, t.rawtx, t.blockheight, t.txindex, t.type as txtype, c2.short_channel_id as txchan, a.location, a.idx as ann_idx, a.type as annotation_type, c.short_channel_id FROM transactions t LEFT JOIN transaction_annotations a ON (a.txid = t.id) LEFT JOIN channels c ON (a.channel = c.id) LEFT JOIN channels c2 ON (t.channel_id = c2.id) ORDER BY t.blockheight, t.txindex ASC" msgstr "" -#: wallet/wallet.c:4299 +#: wallet/wallet.c:4300 msgid "INSERT INTO penalty_bases ( channel_id, commitnum, txid, outnum, amount) VALUES (?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4324 +#: wallet/wallet.c:4325 msgid "SELECT commitnum, txid, outnum, amount FROM penalty_bases WHERE channel_id = ?" msgstr "" -#: wallet/wallet.c:4348 +#: wallet/wallet.c:4349 msgid "DELETE FROM penalty_bases WHERE channel_id = ? AND commitnum = ?" msgstr "" -#: wallet/wallet.c:4366 +#: wallet/wallet.c:4367 msgid "SELECT 1 FROM offers WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4379 +#: wallet/wallet.c:4380 msgid "INSERT INTO offers ( offer_id, bolt12, label, status) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4406 +#: wallet/wallet.c:4407 msgid "SELECT bolt12, label, status FROM offers WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4434 +#: wallet/wallet.c:4435 msgid "SELECT offer_id FROM offers;" msgstr "" -#: wallet/wallet.c:4460 +#: wallet/wallet.c:4461 msgid "UPDATE offers SET status=? WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4471 +#: wallet/wallet.c:4472 msgid "UPDATE invoices SET state=? WHERE state=? AND local_offer_id = ?;" msgstr "" -#: wallet/wallet.c:4499 +#: wallet/wallet.c:4500 msgid "SELECT status FROM offers WHERE offer_id = ?;" msgstr "" @@ -1257,4 +1257,4 @@ msgstr "" #: wallet/test/run-wallet.c:1647 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:5d466ed1c95975f0155bdb67e995c14b7927634e810d319c1ba616327c37d80a +# SHA256STAMP:fdfbb1278ba9e09884c9205e54fc16bb9e66a0362a5aaad11a7efd42746d8e72 diff --git a/wallet/wallet.c b/wallet/wallet.c index 7f53e7d35cfd..70a895e6bf0f 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3176,7 +3176,8 @@ wallet_payment_list(const tal_t *ctx, ", partid" ", local_offer_id" " FROM payments" - " WHERE payment_hash = ?;")); + " WHERE payment_hash = ?" + " ORDER BY id;")); db_bind_sha256(stmt, 0, payment_hash); } else { stmt = db_prepare_v2(wallet->db, SQL("SELECT" From 23fada090cd80675dba158d50d0ebd31935b2996 Mon Sep 17 00:00:00 2001 From: Nalin Bhardwaj Date: Thu, 27 May 2021 03:26:58 +0530 Subject: [PATCH 27/62] plugins/pay: sort output payments in listpays Changelog-Changed: `listpays` output is now ordered by the `created_at` timestamp. --- plugins/pay.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index c3007dc64715..b08c55c64213 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -1701,6 +1702,17 @@ static bool pay_mpp_eq(const struct pay_mpp *pm, const struct sha256 *payment_ha return memcmp(pm->payment_hash, payment_hash, sizeof(struct sha256)) == 0; } +static int cmp_pay_mpp(const struct pay_mpp *a, + const struct pay_mpp *b, + void *unused UNUSED) +{ + if (a->timestamp < b->timestamp) + return -1; + if (a->timestamp == b->timestamp) + return 0; + return 1; +} + HTABLE_DEFINE_TYPE(struct pay_mpp, pay_mpp_key, pay_mpp_hash, pay_mpp_eq, pay_map); @@ -1796,6 +1808,7 @@ static struct command_result *listsendpays_done(struct command *cmd, struct pay_map pay_map; struct pay_map_iter it; struct pay_mpp *pm; + struct pay_mpp *pays; pay_map_init(&pay_map); @@ -1864,17 +1877,26 @@ static struct command_result *listsendpays_done(struct command *cmd, } } - /* Now we've collapsed them, provide summary. */ - ret = jsonrpc_stream_success(cmd); - json_array_start(ret, "pays"); + pays = tal_arr(NULL, struct pay_mpp, pay_map_count(&pay_map)); + i = 0; for (pm = pay_map_first(&pay_map, &it); pm; pm = pay_map_next(&pay_map, &it)) { - add_new_entry(ret, buf, pm); + pays[i++] = *pm; } pay_map_clear(&pay_map); + asort(pays, tal_count(pays), cmp_pay_mpp, NULL); + + /* Now we've collapsed and sorted them, provide summary. */ + ret = jsonrpc_stream_success(cmd); + json_array_start(ret, "pays"); + + for (i = 0; i < tal_count(pays); i++) + add_new_entry(ret, buf, &pays[i]); + tal_free(pays); + json_array_end(ret); return command_finished(cmd, ret); } From a794e87edd6d653e31cb531aeac5a174a3587109 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 May 2021 12:07:30 +0930 Subject: [PATCH 28/62] pytest: test pay ordering. Signed-off-by: Rusty Russell --- tests/test_pay.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index 2223c92feca0..5f75e0f0e02a 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3498,6 +3498,20 @@ def test_listpays_ongoing_attempt(node_factory, bitcoind, executor): l1.rpc.listpays() +def test_listsendpays_and_listpays_order(node_factory): + """listsendpays should be in increasing id order, listpays in created_at""" + l1, l2 = node_factory.line_graph(2) + for i in range(5): + inv = l2.rpc.invoice(1000 - i, "test {}".format(i), "test")['bolt11'] + l1.rpc.pay(inv) + + ids = [p['id'] for p in l1.rpc.listsendpays()['payments']] + assert ids == sorted(ids) + + created_at = [p['created_at'] for p in l1.rpc.listpays()['pays']] + assert created_at == sorted(created_at) + + @pytest.mark.developer("needs use_shadow") def test_mpp_waitblockheight_routehint_conflict(node_factory, bitcoind, executor): ''' From 0b01a8c76f7f8542065d37ed7640f3ff0c73cc9d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 May 2021 12:09:27 +0930 Subject: [PATCH 29/62] doc: document ordering of listpays/listsendpays. Signed-off-by: Rusty Russell --- doc/lightning-listpays.7 | 5 ++++- doc/lightning-listpays.7.md | 2 ++ doc/lightning-listsendpays.7 | 4 ++-- doc/lightning-listsendpays.7.md | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/lightning-listpays.7 b/doc/lightning-listpays.7 index 0d2c828a1f2d..672918f16167 100644 --- a/doc/lightning-listpays.7 +++ b/doc/lightning-listpays.7 @@ -59,6 +59,9 @@ If \fBstatus\fR is "failed": \fBerroronion\fR (hex, optional): the error onion returned on failure, if any\. .RE + +The returned array is ordered by increasing \fBcreated_at\fR fields\. + .SH AUTHOR Rusty Russell \fI is mainly responsible\. @@ -71,4 +74,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:e27d57394bef9bdaf9b99ae0d9050c9044c194ab66f6c94c43b532a86e1a0031 +\" SHA256STAMP:4037a4dd9746b5dfc91ef2f5b4fa9000a334689157d9ac86dc4dba5c82628cfe diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index 0ade89123bb0..aab53b6c0b1d 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -37,6 +37,8 @@ If **status** is "failed": - **erroronion** (hex, optional): the error onion returned on failure, if any. [comment]: # (GENERATE-FROM-SCHEMA-END) +The returned array is ordered by increasing **created_at** fields. + AUTHOR ------ diff --git a/doc/lightning-listsendpays.7 b/doc/lightning-listsendpays.7 index 5cb045fffb72..8f594c82cf26 100644 --- a/doc/lightning-listsendpays.7 +++ b/doc/lightning-listsendpays.7 @@ -18,7 +18,7 @@ command per \fIpay\fR, so this command should be used with caution\. .SH RETURN VALUE -On success, an array of objects is returned\. Each object contains: +On success, an array of objects is returned, ordered by increasing \fIid\fR\. Each object contains: \fIid\fR @@ -75,4 +75,4 @@ responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:693da7a03235656092cc349b6b77335f71c3a1427d88f1cc8007ea7dd64a8e18 +\" SHA256STAMP:c6b36205c8067723bb9edc20f3645754faa9c9d26568d6c899721b43a1e99812 diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index 22a5e0e9952f..7752b7e73591 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -20,7 +20,7 @@ command per *pay*, so this command should be used with caution. RETURN VALUE ------------ -On success, an array of objects is returned. Each object contains: +On success, an array of objects is returned, ordered by increasing *id*. Each object contains: *id* unique internal value assigned at creation From f7adbd5d58f87d526a5e314dc156b5eae6a9d06e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 May 2021 12:37:50 +0930 Subject: [PATCH 30/62] EXPERIMENTAL: import spec for quiescence. Imported from commit b96218b06b68cf349457b282f05d48ebd89c7273 Signed-off-by: Rusty Russell --- channeld/channeld.c | 3 +++ gossipd/gossipd.c | 3 +++ openingd/dualopend.c | 9 +++++++++ wire/extracted_peer_exp_quiescence-protocol.patch | 12 ++++++++++++ wire/peer_wire.c | 6 ++++++ 5 files changed, 33 insertions(+) create mode 100644 wire/extracted_peer_exp_quiescence-protocol.patch diff --git a/channeld/channeld.c b/channeld/channeld.c index 862f5e5a6e8f..e2a41c28e0ec 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1949,6 +1949,9 @@ static void peer_in(struct peer *peer, const u8 *msg) return; case WIRE_INIT_RBF: case WIRE_ACK_RBF: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif break; case WIRE_CHANNEL_REESTABLISH: diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index d47bda25c7fc..5619b5ef7b3d 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -741,6 +741,9 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, case WIRE_ACCEPT_CHANNEL2: case WIRE_INIT_RBF: case WIRE_ACK_RBF: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif status_broken("peer %s: relayed unexpected msg of type %s", type_to_string(tmpctx, struct node_id, &peer->id), peer_wire_name(fromwire_peektype(msg))); diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 640c8ebf29c8..a264bbf208d4 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1273,6 +1273,9 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) case WIRE_WARNING: case WIRE_PING: case WIRE_PONG: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif break; } @@ -1614,6 +1617,9 @@ static bool run_tx_interactive(struct state *state, case WIRE_REPLY_SHORT_CHANNEL_IDS_END: case WIRE_PING: case WIRE_PONG: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif open_err_warn(state, "Unexpected wire message %s", tal_hex(tmpctx, msg)); return false; @@ -3402,6 +3408,9 @@ static u8 *handle_peer_in(struct state *state) case WIRE_WARNING: case WIRE_PING: case WIRE_PONG: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif break; } diff --git a/wire/extracted_peer_exp_quiescence-protocol.patch b/wire/extracted_peer_exp_quiescence-protocol.patch new file mode 100644 index 000000000000..804b726b923f --- /dev/null +++ b/wire/extracted_peer_exp_quiescence-protocol.patch @@ -0,0 +1,12 @@ +--- wire/peer_exp_wire.csv 2021-05-17 09:30:02.302260471 +0930 ++++ - 2021-05-31 12:20:36.390910632 +0930 +@@ -120,6 +82,9 @@ + msgtype,ack_rbf,73 + msgdata,ack_rbf,channel_id,channel_id, + msgdata,ack_rbf,funding_satoshis,u64, ++msgtype,stfu,2 ++msgdata,stfu,channel_id,channel_id, ++msgdata,stfu,initiator,u8, + msgtype,shutdown,38 + msgdata,shutdown,channel_id,channel_id, + msgdata,shutdown,len,u16, diff --git a/wire/peer_wire.c b/wire/peer_wire.c index a79a16a2e9f7..1f8fc3731306 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -44,6 +44,9 @@ static bool unknown_type(enum peer_wire t) case WIRE_ACCEPT_CHANNEL2: case WIRE_INIT_RBF: case WIRE_ACK_RBF: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif return false; } return true; @@ -93,6 +96,9 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_ACCEPT_CHANNEL2: case WIRE_INIT_RBF: case WIRE_ACK_RBF: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif break; } return false; From 40e217872ac14f3aa46e8954681731d6084d364c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 May 2021 12:38:04 +0930 Subject: [PATCH 31/62] channeld: implement pending_updates() Says if we have started an update which is still pending somewhere. Signed-off-by: Rusty Russell --- channeld/full_channel.c | 28 ++++++++++++++++++++++++++++ channeld/full_channel.h | 7 +++++++ 2 files changed, 35 insertions(+) diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 53d504d5c8c0..55ae6214c0b3 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -1252,6 +1252,34 @@ static bool adjust_balance(struct balance view_owed[NUM_SIDES][NUM_SIDES], return true; } +bool pending_updates(const struct channel *channel, enum side side) +{ + struct htlc_map_iter it; + const struct htlc *htlc; + + /* Initiator might have fee changes in play. */ + if (side == channel->opener) { + if (!feerate_changes_done(channel->fee_states)) + return true; + } + + for (htlc = htlc_map_first(channel->htlcs, &it); + htlc; + htlc = htlc_map_next(channel->htlcs, &it)) { + /* If it's still being added, it's owner added it. */ + if (htlc_state_flags(htlc->state) & HTLC_ADDING) { + if (htlc_owner(htlc) == side) + return true; + /* If it's being removed, non-owner removed it */ + } else if (htlc_state_flags(htlc->state) & HTLC_REMOVING) { + if (htlc_owner(htlc) != side) + return true; + } + } + + return false; +} + bool channel_force_htlcs(struct channel *channel, const struct existing_htlc **htlcs) { diff --git a/channeld/full_channel.h b/channeld/full_channel.h index 11a43451fbbf..8f04547eb70b 100644 --- a/channeld/full_channel.h +++ b/channeld/full_channel.h @@ -254,6 +254,13 @@ bool channel_force_htlcs(struct channel *channel, */ void dump_htlcs(const struct channel *channel, const char *prefix); +/** + * pending_updates: does this side have updates pending in channel? + * @channel: the channel + * @side: the side who is offering or failing/fulfilling HTLC, or feechange + */ +bool pending_updates(const struct channel *channel, enum side side); + const char *channel_add_err_name(enum channel_add_err e); const char *channel_remove_err_name(enum channel_remove_err e); From e29c9c2fc012d5ff11cfc0999d56d015628076af Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 May 2021 12:38:04 +0930 Subject: [PATCH 32/62] EXPERIMENTAL: handle receiving quiescence request. Changelog-EXPERIMENTAL: Protocol: we support the quiescence protocol from https://github.com/lightningnetwork/lightning-rfc/pull/869 Signed-off-by: Rusty Russell --- channeld/channeld.c | 139 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 3 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index e2a41c28e0ec..fc36fee04bb0 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -159,6 +159,17 @@ struct peer { /* If master told us to send wrong_funding */ struct bitcoin_outpoint *shutdown_wrong_funding; +#if EXPERIMENTAL_FEATURES + /* Do we want quiescence? */ + bool stfu; + /* Which side is considered the initiator? */ + enum side stfu_initiator; + /* Has stfu been sent by each side? */ + bool stfu_sent[NUM_SIDES]; + /* Updates master asked, which we've deferred while quiescing */ + struct msg_queue *update_queue; +#endif + /* Information used for reestablishment. */ bool last_was_revoke; struct changed_htlc *last_sent_commit; @@ -273,6 +284,99 @@ static struct amount_msat advertized_htlc_max(const struct channel *channel) return lower_bound_msat; } +#if EXPERIMENTAL_FEATURES +static void maybe_send_stfu(struct peer *peer) +{ + if (!peer->stfu) + return; + + if (!peer->stfu_sent[LOCAL] && !pending_updates(peer->channel, LOCAL)) { + u8 *msg = towire_stfu(NULL, &peer->channel_id, + peer->stfu_initiator == LOCAL); + sync_crypto_write(peer->pps, take(msg)); + peer->stfu_sent[LOCAL] = true; + } + + /* FIXME: We're finished, do something! */ + if (peer->stfu_sent[LOCAL] && peer->stfu_sent[REMOTE]) + status_unusual("STFU complete: we are quiescent"); +} + +static void handle_stfu(struct peer *peer, const u8 *stfu) +{ + struct channel_id channel_id; + u8 remote_initiated; + + if (!fromwire_stfu(stfu, &channel_id, &remote_initiated)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad stfu %s", tal_hex(peer, stfu)); + + if (!channel_id_eq(&channel_id, &peer->channel_id)) { + peer_failed_err(peer->pps, &channel_id, + "Wrong stfu channel_id: expected %s, got %s", + type_to_string(tmpctx, struct channel_id, + &peer->channel_id), + type_to_string(tmpctx, struct channel_id, + &channel_id)); + } + + /* Sanity check */ + if (pending_updates(peer->channel, REMOTE)) + peer_failed_warn(peer->pps, &peer->channel_id, + "STFU but you still have updates pending?"); + + if (!peer->stfu) { + peer->stfu = true; + if (!remote_initiated) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unsolicited STFU but you said" + " you didn't initiate?"); + peer->stfu_initiator = REMOTE; + } else { + /* BOLT-quiescent #2: + * + * If both sides send `stfu` simultaneously, they will both + * set `initiator` to `1`, in which case the "initiator" is + * arbitrarily considered to be the channel funder (the sender + * of `open_channel`). + */ + if (remote_initiated) + peer->stfu_initiator = peer->channel->opener; + } + + /* BOLT-quiescent #2: + * The receiver of `stfu`: + * - if it has sent `stfu` then: + * - MUST now consider the channel to be quiescent + * - otherwise: + * - SHOULD NOT send any more update messages. + * - MUST reply with `stfu` once it can do so. + */ + peer->stfu_sent[REMOTE] = true; + + maybe_send_stfu(peer); +} + +/* Returns true if we queued this for later handling (steals if true) */ +static bool handle_master_request_later(struct peer *peer, const u8 *msg) +{ + if (peer->stfu) { + msg_enqueue(peer->update_queue, take(msg)); + return true; + } + return false; +} +#else /* !EXPERIMENTAL_FEATURES */ +static bool handle_master_request_later(struct peer *peer, const u8 *msg) +{ + return false; +} + +static void maybe_send_stfu(struct peer *peer) +{ +} +#endif + /* Create and send channel_update to gossipd (and maybe peer) */ static void send_channel_update(struct peer *peer, int disable_flag) { @@ -952,6 +1056,12 @@ static bool want_fee_update(const struct peer *peer, u32 *target) if (peer->channel->opener != LOCAL) return false; +#if EXPERIMENTAL_FEATURES + /* No fee update while quiescing! */ + if (peer->stfu) + return false; +#endif + max = approx_max_feerate(peer->channel); val = peer->desired_feerate; @@ -1408,6 +1518,9 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) send_revocation(peer, &commit_sig, htlc_sigs, changed_htlcs, txs[0]); + /* We may now be quiescent on our side. */ + maybe_send_stfu(peer); + /* This might have synced the feerates: if so, we may want to * update */ if (want_fee_update(peer, NULL)) @@ -1537,6 +1650,9 @@ static void handle_peer_revoke_and_ack(struct peer *peer, const u8 *msg) type_to_string(tmpctx, struct pubkey, &peer->old_remote_per_commit)); + /* We may now be quiescent on our side. */ + maybe_send_stfu(peer); + start_commit_timer(peer); } @@ -1931,6 +2047,11 @@ static void peer_in(struct peer *peer, const u8 *msg) handle_peer_shutdown(peer, msg); return; +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: + handle_stfu(peer, msg); + return; +#endif case WIRE_INIT: case WIRE_OPEN_CHANNEL: case WIRE_ACCEPT_CHANNEL: @@ -1949,9 +2070,6 @@ static void peer_in(struct peer *peer, const u8 *msg) return; case WIRE_INIT_RBF: case WIRE_ACK_RBF: -#if EXPERIMENTAL_FEATURES - case WIRE_STFU: -#endif break; case WIRE_CHANNEL_REESTABLISH: @@ -2972,18 +3090,28 @@ static void req_in(struct peer *peer, const u8 *msg) handle_funding_depth(peer, msg); return; case WIRE_CHANNELD_OFFER_HTLC: + if (handle_master_request_later(peer, msg)) + return; handle_offer_htlc(peer, msg); return; case WIRE_CHANNELD_FEERATES: + if (handle_master_request_later(peer, msg)) + return; handle_feerates(peer, msg); return; case WIRE_CHANNELD_FULFILL_HTLC: + if (handle_master_request_later(peer, msg)) + return; handle_preimage(peer, msg); return; case WIRE_CHANNELD_FAIL_HTLC: + if (handle_master_request_later(peer, msg)) + return; handle_fail(peer, msg); return; case WIRE_CHANNELD_SPECIFIC_FEERATES: + if (handle_master_request_later(peer, msg)) + return; handle_specific_feerates(peer, msg); return; case WIRE_CHANNELD_SEND_SHUTDOWN: @@ -3266,6 +3394,11 @@ int main(int argc, char *argv[]) /* We actually received it in the previous daemon, but near enough */ peer->last_recv = time_now(); peer->last_empty_commitment = 0; +#if EXPERIMENTAL_FEATURES + peer->stfu = false; + peer->stfu_sent[LOCAL] = peer->stfu_sent[REMOTE] = false; + peer->update_queue = msg_queue_new(peer); +#endif /* We send these to HSM to get real signatures; don't have valgrind * complain. */ From 03cfe0b468a550719918a7b56c0389e16c2cffb1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 May 2021 12:38:04 +0930 Subject: [PATCH 33/62] EXPERIMENTAL: dev-quiesce to initiate (and test) quiescence. Signed-off-by: Rusty Russell --- channeld/channeld.c | 29 ++++++++++++++++++-- channeld/channeld_wire.csv | 4 +++ channeld/channeld_wiregen.c | 45 ++++++++++++++++++++++++++++++- channeld/channeld_wiregen.h | 14 +++++++++- lightningd/channel_control.c | 52 ++++++++++++++++++++++++++++++++++++ tests/test_connection.py | 24 +++++++++++++++++ 6 files changed, 164 insertions(+), 4 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index fc36fee04bb0..d33e9da1e2fe 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -297,9 +297,11 @@ static void maybe_send_stfu(struct peer *peer) peer->stfu_sent[LOCAL] = true; } - /* FIXME: We're finished, do something! */ - if (peer->stfu_sent[LOCAL] && peer->stfu_sent[REMOTE]) + if (peer->stfu_sent[LOCAL] && peer->stfu_sent[REMOTE]) { status_unusual("STFU complete: we are quiescent"); + wire_sync_write(MASTER_FD, + towire_channeld_dev_quiesce_reply(tmpctx)); + } } static void handle_stfu(struct peer *peer, const u8 *stfu) @@ -3079,6 +3081,22 @@ static void channeld_send_custommsg(struct peer *peer, const u8 *msg) master_badmsg(WIRE_CUSTOMMSG_OUT, msg); sync_crypto_write(peer->pps, take(inner)); } + +#if EXPERIMENTAL_FEATURES +static void handle_dev_quiesce(struct peer *peer, const u8 *msg) +{ + if (!fromwire_channeld_dev_quiesce(msg)) + master_badmsg(WIRE_CHANNELD_DEV_QUIESCE, msg); + + /* Don't do this twice. */ + if (peer->stfu) + status_failed(STATUS_FAIL_MASTER_IO, "dev_quiesce already"); + + peer->stfu = true; + peer->stfu_initiator = LOCAL; + maybe_send_stfu(peer); +} +#endif /* EXPERIMENTAL_FEATURES */ #endif /* DEVELOPER */ static void req_in(struct peer *peer, const u8 *msg) @@ -3127,9 +3145,15 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_DEV_MEMLEAK: handle_dev_memleak(peer, msg); return; + case WIRE_CHANNELD_DEV_QUIESCE: +#if EXPERIMENTAL_FEATURES + handle_dev_quiesce(peer, msg); + return; +#endif /* EXPERIMENTAL_FEATURES */ #else case WIRE_CHANNELD_DEV_REENABLE_COMMIT: case WIRE_CHANNELD_DEV_MEMLEAK: + case WIRE_CHANNELD_DEV_QUIESCE: #endif /* DEVELOPER */ case WIRE_CHANNELD_INIT: case WIRE_CHANNELD_OFFER_HTLC_REPLY: @@ -3147,6 +3171,7 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_FAIL_FALLEN_BEHIND: case WIRE_CHANNELD_DEV_MEMLEAK_REPLY: case WIRE_CHANNELD_SEND_ERROR_REPLY: + case WIRE_CHANNELD_DEV_QUIESCE_REPLY: break; } diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 25083fb088ed..a376f5a6536f 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -217,3 +217,7 @@ msgdata,channeld_send_error,reason,wirestring, # Tell master channeld has sent the error message. msgtype,channeld_send_error_reply,1108 + +# Ask channeld to quiesce. +msgtype,channeld_dev_quiesce,1009 +msgtype,channeld_dev_quiesce_reply,1109 diff --git a/channeld/channeld_wiregen.c b/channeld/channeld_wiregen.c index 11bc0e143c5f..4d4fe6c676e4 100644 --- a/channeld/channeld_wiregen.c +++ b/channeld/channeld_wiregen.c @@ -46,6 +46,8 @@ const char *channeld_wire_name(int e) case WIRE_CHANNELD_GOT_ANNOUNCEMENT: return "WIRE_CHANNELD_GOT_ANNOUNCEMENT"; case WIRE_CHANNELD_SEND_ERROR: return "WIRE_CHANNELD_SEND_ERROR"; case WIRE_CHANNELD_SEND_ERROR_REPLY: return "WIRE_CHANNELD_SEND_ERROR_REPLY"; + case WIRE_CHANNELD_DEV_QUIESCE: return "WIRE_CHANNELD_DEV_QUIESCE"; + case WIRE_CHANNELD_DEV_QUIESCE_REPLY: return "WIRE_CHANNELD_DEV_QUIESCE_REPLY"; } snprintf(invalidbuf, sizeof(invalidbuf), "INVALID %i", e); @@ -81,6 +83,8 @@ bool channeld_wire_is_defined(u16 type) case WIRE_CHANNELD_GOT_ANNOUNCEMENT:; case WIRE_CHANNELD_SEND_ERROR:; case WIRE_CHANNELD_SEND_ERROR_REPLY:; + case WIRE_CHANNELD_DEV_QUIESCE:; + case WIRE_CHANNELD_DEV_QUIESCE_REPLY:; return true; } return false; @@ -1070,4 +1074,43 @@ bool fromwire_channeld_send_error_reply(const void *p) return false; return cursor != NULL; } -// SHA256STAMP:60143693b0c3611c8ecdf7f3549ef9f4c280e359cac0cd1f4df38cdca2dad3cb + +/* WIRE: CHANNELD_DEV_QUIESCE */ +/* Ask channeld to quiesce. */ +u8 *towire_channeld_dev_quiesce(const tal_t *ctx) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_CHANNELD_DEV_QUIESCE); + + return memcheck(p, tal_count(p)); +} +bool fromwire_channeld_dev_quiesce(const void *p) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_CHANNELD_DEV_QUIESCE) + return false; + return cursor != NULL; +} + +/* WIRE: CHANNELD_DEV_QUIESCE_REPLY */ +u8 *towire_channeld_dev_quiesce_reply(const tal_t *ctx) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_CHANNELD_DEV_QUIESCE_REPLY); + + return memcheck(p, tal_count(p)); +} +bool fromwire_channeld_dev_quiesce_reply(const void *p) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_CHANNELD_DEV_QUIESCE_REPLY) + return false; + return cursor != NULL; +} +// SHA256STAMP:720f9917311384d373593dc1550619ddf461bdabde8b312ed6dc632cb7860c34 diff --git a/channeld/channeld_wiregen.h b/channeld/channeld_wiregen.h index 3565517070f2..7d6f16c5426d 100644 --- a/channeld/channeld_wiregen.h +++ b/channeld/channeld_wiregen.h @@ -70,6 +70,9 @@ enum channeld_wire { WIRE_CHANNELD_SEND_ERROR = 1008, /* Tell master channeld has sent the error message. */ WIRE_CHANNELD_SEND_ERROR_REPLY = 1108, + /* Ask channeld to quiesce. */ + WIRE_CHANNELD_DEV_QUIESCE = 1009, + WIRE_CHANNELD_DEV_QUIESCE_REPLY = 1109, }; const char *channeld_wire_name(int e); @@ -211,6 +214,15 @@ bool fromwire_channeld_send_error(const tal_t *ctx, const void *p, wirestring ** u8 *towire_channeld_send_error_reply(const tal_t *ctx); bool fromwire_channeld_send_error_reply(const void *p); +/* WIRE: CHANNELD_DEV_QUIESCE */ +/* Ask channeld to quiesce. */ +u8 *towire_channeld_dev_quiesce(const tal_t *ctx); +bool fromwire_channeld_dev_quiesce(const void *p); + +/* WIRE: CHANNELD_DEV_QUIESCE_REPLY */ +u8 *towire_channeld_dev_quiesce_reply(const tal_t *ctx); +bool fromwire_channeld_dev_quiesce_reply(const void *p); + #endif /* LIGHTNING_CHANNELD_CHANNELD_WIREGEN_H */ -// SHA256STAMP:60143693b0c3611c8ecdf7f3549ef9f4c280e359cac0cd1f4df38cdca2dad3cb +// SHA256STAMP:720f9917311384d373593dc1550619ddf461bdabde8b312ed6dc632cb7860c34 diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 3958e9dfa1b7..bf9f91cf52ff 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -425,11 +425,13 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_FEERATES: case WIRE_CHANNELD_SPECIFIC_FEERATES: case WIRE_CHANNELD_DEV_MEMLEAK: + case WIRE_CHANNELD_DEV_QUIESCE: /* Replies go to requests. */ case WIRE_CHANNELD_OFFER_HTLC_REPLY: case WIRE_CHANNELD_DEV_REENABLE_COMMIT_REPLY: case WIRE_CHANNELD_DEV_MEMLEAK_REPLY: case WIRE_CHANNELD_SEND_ERROR: + case WIRE_CHANNELD_DEV_QUIESCE_REPLY: break; } @@ -937,4 +939,54 @@ static const struct json_command dev_feerate_command = { "Set feerate for {id} to {feerate}" }; AUTODATA(json_command, &dev_feerate_command); + +#if EXPERIMENTAL_FEATURES +static void quiesce_reply(struct subd *channeld UNUSED, + const u8 *reply, + const int *fds UNUSED, + struct command *cmd) +{ + struct json_stream *response; + + response = json_stream_success(cmd); + was_pending(command_success(cmd, response)); +} + +static struct command_result *json_dev_quiesce(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct node_id *id; + struct peer *peer; + struct channel *channel; + const u8 *msg; + + if (!param(cmd, buffer, params, + p_req("id", param_node_id, &id), + NULL)) + return command_param_failed(); + + peer = peer_by_id(cmd->ld, id); + if (!peer) + return command_fail(cmd, LIGHTNINGD, "Peer not connected"); + + channel = peer_active_channel(peer); + if (!channel || !channel->owner || channel->state != CHANNELD_NORMAL) + return command_fail(cmd, LIGHTNINGD, "Peer bad state"); + + msg = towire_channeld_dev_quiesce(NULL); + subd_req(channel->owner, channel->owner, take(msg), -1, 0, + quiesce_reply, cmd); + return command_still_pending(cmd); +} + +static const struct json_command dev_quiesce_command = { + "dev-quiesce", + "developer", + json_dev_quiesce, + "Initiate quiscence protocol with peer" +}; +AUTODATA(json_command, &dev_quiesce_command); +#endif /* EXPERIMENTAL_FEATURES */ #endif /* DEVELOPER */ diff --git a/tests/test_connection.py b/tests/test_connection.py index a44103d2933b..b4368a0903bd 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3470,6 +3470,30 @@ def test_openchannel_init_alternate(node_factory, executor): fut.result(10) +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "quiescence is experimental") +@pytest.mark.developer("quiescence triggering is dev only") +def test_quiescence(node_factory, executor): + l1, l2 = node_factory.line_graph(2) + + # Works fine. + l1.pay(l2, 1000) + + assert l1.rpc.call('dev-quiesce', [l2.info['id']]) == {} + + # Both should consider themselves quiescent. + l1.daemon.wait_for_log("STFU complete: we are quiescent") + l2.daemon.wait_for_log("STFU complete: we are quiescent") + + # Should not be able to increase fees. + l1.rpc.call('dev-feerate', [l2.info['id'], 9999]) + + try: + l1.daemon.wait_for_log('peer_out WIRE_UPDATE_FEE', 5) + assert False + except TimeoutError: + pass + + def test_htlc_failed_noclose(node_factory): """Test a bug where the htlc timeout would kick in even if the HTLC failed""" l1, l2 = node_factory.line_graph(2) From 3c4c8d4fa014c8b716f54cb703cdfd7e5a9e9ea0 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 1 Jun 2021 12:14:48 -0500 Subject: [PATCH 34/62] libwally: update to release_0.8.3 Remove hacks to get around empty PSBT deserialization bug --- bitcoin/psbt.c | 8 -------- common/setup.c | 1 + external/libwally-core | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index c3f294e39291..df76d4e6cd34 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -689,10 +689,6 @@ struct wally_psbt *psbt_from_b64(const tal_t *ctx, psbt = NULL; tal_wally_end(tal_steal(ctx, psbt)); - /* FIXME: Patch for the empty-tx bug in libwally */ - if (!psbt && strlen(str) == 28) - psbt = create_psbt(ctx, 0, 0, 0); - return psbt; } @@ -744,10 +740,6 @@ struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes, psbt = NULL; tal_wally_end(tal_steal(ctx, psbt)); - /* FIXME: Patch for the empty-tx bug in libwally */ - if (!psbt && byte_len == 19) - psbt = create_psbt(ctx, 0, 0, 0); - return psbt; } diff --git a/common/setup.c b/common/setup.c index 2ea41f5a2f5e..88fb25b4a569 100644 --- a/common/setup.c +++ b/common/setup.c @@ -19,6 +19,7 @@ static void wally_free(void *ptr) } static struct wally_operations wally_tal_ops = { + .struct_size = sizeof(struct wally_operations), .malloc_fn = wally_tal, .free_fn = wally_free, }; diff --git a/external/libwally-core b/external/libwally-core index bf81e8b17069..46a3db9b7bce 160000 --- a/external/libwally-core +++ b/external/libwally-core @@ -1 +1 @@ -Subproject commit bf81e8b17069020dec0638e352298770aa382952 +Subproject commit 46a3db9b7bce9179430d81ee10bcd25ace5616e4 From 5d69964dde97a52145edcb3ba251f57f6671e4c6 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 1 Jun 2021 12:20:26 -0500 Subject: [PATCH 35/62] reqs: Remove wallycore, no longer needed for CI tests With the migration from a python plugin to the `funder` plugin, we removed our dependency on wallycore python libs --- requirements.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 006d91723cc9..ef5e2b9dcb5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,4 @@ # Dependencies required to build and test c-lightning -https://github.com/ElementsProject/libwally-core/releases/download/release_0.8.0/wallycore-0.8.0-cp36-cp36m-linux_x86_64.whl; 'linux' in sys_platform and python_version == '3.6' -https://github.com/ElementsProject/libwally-core/releases/download/release_0.8.0/wallycore-0.8.0-cp37-cp37m-linux_x86_64.whl; 'linux' in sys_platform and python_version == '3.7' -https://github.com/ElementsProject/libwally-core/releases/download/release_0.8.0/wallycore-0.8.0-cp37-cp37m-macosx_10_14_x86_64.whl; sys_platform == 'darwin' and python_version == '3.7' mrkd ~= 0.1.6 Mako ~= 1.1.3 flake8 ~= 3.7.8 From b5aaee667951dabcece3561217d7608b052e2fbf Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Tue, 1 Jun 2021 13:21:57 -0400 Subject: [PATCH 36/62] Revert "pytest: Skip hsm encryption test if we don't have a TTY" This reverts commit 2b12cac31ef636191d9dafec58bb56b960491c6d. There is no need to skip the test in this case, and it seems to simply be an artifact of CI-debugging hell :) Changelog-None --- tests/test_wallet.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 4cb78b2f4568..5e1b03bcd98e 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -12,7 +12,6 @@ import os import pytest import subprocess -import sys import unittest @@ -992,7 +991,6 @@ def test_transaction_annotations(node_factory, bitcoind): @unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.") -@unittest.skipIf(not sys.stdout.isatty(), "Cannot") def test_hsm_secret_encryption(node_factory): l1 = node_factory.get_node(may_fail=True) # May fail when started without key password = "reckful\n" From e95fc7488416b6273c2553f5d3ce2ace788cba71 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 1 Jun 2021 11:12:03 -0500 Subject: [PATCH 37/62] tests: cleanup any unfinished/hanging channel opens Otherwise it hangs on cleanup, as l2 is waiting for l1 to respond (and it's not going to), so the memleak call hangs. --- tests/test_connection.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index b4368a0903bd..e2ede44dfe8a 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3463,12 +3463,20 @@ def test_openchannel_init_alternate(node_factory, executor): psbt1 = l1.rpc.fundpsbt('1000000msat', '253perkw', 250)['psbt'] psbt2 = l2.rpc.fundpsbt('1000000msat', '253perkw', 250)['psbt'] - l1.rpc.openchannel_init(l2.info['id'], 100000, psbt1) + init = l1.rpc.openchannel_init(l2.info['id'], 100000, psbt1) fut = executor.submit(l2.rpc.openchannel_init, l1.info['id'], '1000000msat', psbt2) with pytest.raises(RpcError): fut.result(10) + # FIXME: Clean up so it doesn't hang. Ok if these fail. + for node in [l1, l2]: + try: + node.rpc.openchannel_abort(init['channel_id']) + except RpcError: + # Ignoring all errors + print("nothing to do") + @unittest.skipIf(not EXPERIMENTAL_FEATURES, "quiescence is experimental") @pytest.mark.developer("quiescence triggering is dev only") From cead5e16c0c556308fce3a54f74fd5dc3e8ea992 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 1 Jun 2021 14:38:55 -0500 Subject: [PATCH 38/62] openingd: return more informative error message for v2 opens Fixes #4562 Reported-By: William Casarin @jb55 New error message: zircon:lightning (nifty/fix-4562)$ l1-cli fundchannel_start 03fce775508719e4064b7f19d4e884ddaf51db23bbfc560286ce872f9ed106fee0 10000 253perkw { "code": 312, "message": "Peer negotiated `option_dual_fund`, must use `openchannel_init` not `fundchannel_start`." } --- lightningd/opening_control.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 3d26add0fc0e..df6336c1f156 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1177,6 +1177,15 @@ static struct command_result *json_fundchannel_start(struct command *cmd, } if (!peer->uncommitted_channel) { + if (feature_negotiated(cmd->ld->our_features, + peer->their_features, + OPT_DUAL_FUND)) + return command_fail(cmd, FUNDING_STATE_INVALID, + "Peer negotiated" + " `option_dual_fund`," + " must use `openchannel_init` not" + " `fundchannel_start`."); + return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, "Peer not connected"); } From e4241800581fde27b5c60c5be5c7aa703a5a2a26 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Sun, 16 May 2021 12:56:34 -0300 Subject: [PATCH 39/62] remove checking for unused "direction" property in sendpay. --- lightningd/pay.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index 4793273f1143..5a81280266e1 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1187,13 +1187,12 @@ static struct command_result * param_route_hop(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct route_hop **hop) { - const jsmntok_t *idtok, *channeltok, *directiontok, *amounttok, *delaytok; + const jsmntok_t *idtok, *channeltok, *amounttok, *delaytok; struct route_hop *res; res = tal(cmd, struct route_hop); idtok = json_get_member(buffer, tok, "id"); channeltok = json_get_member(buffer, tok, "channel"); - directiontok = json_get_member(buffer, tok, "direction"); amounttok = json_get_member(buffer, tok, "amount_msat"); delaytok = json_get_member(buffer, tok, "delay"); @@ -1203,11 +1202,6 @@ param_route_hop(struct command *cmd, const char *name, const char *buffer, cmd, JSONRPC2_INVALID_PARAMS, "Either 'id' or 'channel' is required for a route_hop"); - if (channeltok && !directiontok) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "When specifying a channel you must also " - "specify the direction"); - if (!amounttok) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "'amount_msat' is required"); @@ -1232,11 +1226,6 @@ param_route_hop(struct command *cmd, const char *name, const char *buffer, "should be a short_channel_id"); } - if (directiontok && (!json_to_int(buffer, directiontok, &res->direction) || - res->direction > 1 || res->direction < 0)) - return command_fail_badparam(cmd, name, buffer, directiontok, - "should be 0 or 1"); - if (!json_to_msat(buffer, amounttok, &res->amount)) return command_fail_badparam(cmd, name, buffer, amounttok, "should be a valid amount_msat"); @@ -1358,6 +1347,7 @@ static struct command_result *param_route_hops(struct command *cmd, p_opt("id", param_node_id, &id), p_opt("delay", param_number, &delay), p_opt("channel", param_short_channel_id, &channel), + /* Allowed (getroute supplies it) but ignored */ p_opt("direction", param_number, &direction), p_opt("style", param_route_hop_style, &style), p_opt("blinding", param_pubkey, &blinding), @@ -1401,8 +1391,6 @@ static struct command_result *param_route_hops(struct command *cmd, (*hops)[i].blinding = blinding; (*hops)[i].enctlv = enctlv; (*hops)[i].style = style ? *style : default_style; - /* FIXME: Actually ignored by sending code! */ - (*hops)[i].direction = direction ? *direction : 0; } return NULL; From 7cffea10ce50252a1201e83b53517978b962bff9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jun 2021 11:31:16 +0930 Subject: [PATCH 40/62] connectd: new command to make us send message and close. Currently we abuse openingd and dualopend to do this, but connectd already has the ability to talk to peers, so it's more efficient. Signed-off-by: Rusty Russell --- connectd/connectd.c | 82 +++++++++++++++++++++++++++++++----- connectd/connectd_wire.csv | 7 +++ connectd/connectd_wiregen.c | 37 +++++++++++++++- connectd/connectd_wiregen.h | 9 +++- lightningd/connect_control.c | 1 + 5 files changed, 124 insertions(+), 12 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index f932f46a3fb2..f92f2e187cd7 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1591,23 +1591,19 @@ static struct io_plan *connect_to_peer(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->master); } -/* lightningd tells us a peer has disconnected. */ -static struct io_plan *peer_disconnected(struct io_conn *conn, - struct daemon *daemon, const u8 *msg) +/* A peer is gone: clean things up. */ +static void cleanup_dead_peer(struct daemon *daemon, const struct node_id *id) { - struct node_id id, *node; - - if (!fromwire_connectd_peer_disconnected(msg, &id)) - master_badmsg(WIRE_CONNECTD_PEER_DISCONNECTED, msg); + struct node_id *node; /* We should stay in sync with lightningd at all times. */ - node = node_set_get(&daemon->peers, &id); + node = node_set_get(&daemon->peers, id); if (!node) status_failed(STATUS_FAIL_INTERNAL_ERROR, "peer_disconnected unknown peer: %s", - type_to_string(tmpctx, struct node_id, &id)); + type_to_string(tmpctx, struct node_id, id)); node_set_del(&daemon->peers, node); - status_peer_debug(&id, "disconnect"); + status_peer_debug(id, "disconnect"); /* Wake up in case there's a reconnecting peer waiting in io_wait. */ io_wake(node); @@ -1615,6 +1611,69 @@ static struct io_plan *peer_disconnected(struct io_conn *conn, /* Note: deleting from a htable (a-la node_set_del) does not free it: * htable doesn't assume it's a tal object at all. */ tal_free(node); +} + +/* lightningd tells us a peer has disconnected. */ +static struct io_plan *peer_disconnected(struct io_conn *conn, + struct daemon *daemon, const u8 *msg) +{ + struct node_id id; + + if (!fromwire_connectd_peer_disconnected(msg, &id)) + master_badmsg(WIRE_CONNECTD_PEER_DISCONNECTED, msg); + + cleanup_dead_peer(daemon, &id); + + /* Read the next message from lightningd. */ + return daemon_conn_read_next(conn, daemon->master); +} + +/* lightningd tells us to send a final (usually error) message to peer, then + * disconnect. */ +struct final_msg_data { + struct daemon *daemon; + struct node_id id; +}; + +static void destroy_final_msg_data(struct final_msg_data *f) +{ + cleanup_dead_peer(f->daemon, &f->id); +} + +static struct io_plan *send_final_msg(struct io_conn *conn, u8 *msg) +{ + return io_write(conn, msg, tal_bytelen(msg), io_close_cb, NULL); +} + +/* lightningd tells us to send a msg and disconnect. */ +static struct io_plan *peer_final_msg(struct io_conn *conn, + struct daemon *daemon, const u8 *msg) +{ + struct per_peer_state *pps; + struct final_msg_data *f = tal(NULL, struct final_msg_data); + u8 *finalmsg; + int fds[3]; + + /* pps is allocated off f, so fds are closed when f freed. */ + if (!fromwire_connectd_peer_final_msg(f, msg, &f->id, &pps, &finalmsg)) + master_badmsg(WIRE_CONNECTD_PEER_FINAL_MSG, msg); + + /* When f is freed, we want to mark node as dead. */ + tal_add_destructor(f, destroy_final_msg_data); + + /* Get the fds for this peer. */ + for (size_t i = 0; i < ARRAY_SIZE(fds); i++) + fds[i] = fdpass_recv(io_conn_fd(conn)); + /* We put peer fd into conn, but pps needs to free the rest */ + per_peer_state_set_fds(pps, -1, fds[1], fds[2]); + + /* Log and encrypt message for peer. */ + status_peer_io(LOG_IO_OUT, &f->id, finalmsg); + finalmsg = cryptomsg_encrypt_msg(f, &pps->cs, take(finalmsg)); + + /* Organize io loop to write out that message, it will free f + * once closed */ + tal_steal(io_new_conn(daemon, fds[0], send_final_msg, finalmsg), f); /* Read the next message from lightningd. */ return daemon_conn_read_next(conn, daemon->master); @@ -1662,6 +1721,9 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_PEER_DISCONNECTED: return peer_disconnected(conn, daemon, msg); + case WIRE_CONNECTD_PEER_FINAL_MSG: + return peer_final_msg(conn, daemon, msg); + case WIRE_CONNECTD_DEV_MEMLEAK: #if DEVELOPER return dev_connect_memleak(conn, daemon, msg); diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 95f7abcdf63f..093f7d33f2d7 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -65,6 +65,13 @@ msgdata,connectd_peer_connected,features,u8,flen msgtype,connectd_peer_disconnected,2015 msgdata,connectd_peer_disconnected,id,node_id, +# master -> connectd: give message to peer and disconnect. Three fds: peer, gossip and gossip_store +msgtype,connectd_peer_final_msg,2003 +msgdata,connectd_peer_final_msg,id,node_id, +msgdata,connectd_peer_final_msg,pps,per_peer_state, +msgdata,connectd_peer_final_msg,len,u16, +msgdata,connectd_peer_final_msg,msg,u8,len + # master -> connectd: do you have a memleak? msgtype,connectd_dev_memleak,2033 diff --git a/connectd/connectd_wiregen.c b/connectd/connectd_wiregen.c index 7df76a964f62..a76e3e007f5a 100644 --- a/connectd/connectd_wiregen.c +++ b/connectd/connectd_wiregen.c @@ -29,6 +29,7 @@ const char *connectd_wire_name(int e) case WIRE_CONNECTD_CONNECT_FAILED: return "WIRE_CONNECTD_CONNECT_FAILED"; case WIRE_CONNECTD_PEER_CONNECTED: return "WIRE_CONNECTD_PEER_CONNECTED"; case WIRE_CONNECTD_PEER_DISCONNECTED: return "WIRE_CONNECTD_PEER_DISCONNECTED"; + case WIRE_CONNECTD_PEER_FINAL_MSG: return "WIRE_CONNECTD_PEER_FINAL_MSG"; case WIRE_CONNECTD_DEV_MEMLEAK: return "WIRE_CONNECTD_DEV_MEMLEAK"; case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: return "WIRE_CONNECTD_DEV_MEMLEAK_REPLY"; } @@ -49,6 +50,7 @@ bool connectd_wire_is_defined(u16 type) case WIRE_CONNECTD_CONNECT_FAILED:; case WIRE_CONNECTD_PEER_CONNECTED:; case WIRE_CONNECTD_PEER_DISCONNECTED:; + case WIRE_CONNECTD_PEER_FINAL_MSG:; case WIRE_CONNECTD_DEV_MEMLEAK:; case WIRE_CONNECTD_DEV_MEMLEAK_REPLY:; return true; @@ -368,6 +370,39 @@ bool fromwire_connectd_peer_disconnected(const void *p, struct node_id *id) return cursor != NULL; } +/* WIRE: CONNECTD_PEER_FINAL_MSG */ +/* master -> connectd: give message to peer and disconnect. Three fds: peer */ +u8 *towire_connectd_peer_final_msg(const tal_t *ctx, const struct node_id *id, const struct per_peer_state *pps, const u8 *msg) +{ + u16 len = tal_count(msg); + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_CONNECTD_PEER_FINAL_MSG); + towire_node_id(&p, id); + towire_per_peer_state(&p, pps); + towire_u16(&p, len); + towire_u8_array(&p, msg, len); + + return memcheck(p, tal_count(p)); +} +bool fromwire_connectd_peer_final_msg(const tal_t *ctx, const void *p, struct node_id *id, struct per_peer_state **pps, u8 **msg) +{ + u16 len; + + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_CONNECTD_PEER_FINAL_MSG) + return false; + fromwire_node_id(&cursor, &plen, id); + *pps = fromwire_per_peer_state(ctx, &cursor, &plen); + len = fromwire_u16(&cursor, &plen); + // 2nd case msg + *msg = len ? tal_arr(ctx, u8, len) : NULL; + fromwire_u8_array(&cursor, &plen, *msg, len); + return cursor != NULL; +} + /* WIRE: CONNECTD_DEV_MEMLEAK */ /* master -> connectd: do you have a memleak? */ u8 *towire_connectd_dev_memleak(const tal_t *ctx) @@ -408,4 +443,4 @@ bool fromwire_connectd_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:9bbb0b97a226bd5c85a21bafde42c7fd438b8107d6d30b7c7b17c16a6cbd3557 +// SHA256STAMP:7c9585941825eab65d734eb1c233eee5c78b5792e19ec68f0a9986abca2b0ffe diff --git a/connectd/connectd_wiregen.h b/connectd/connectd_wiregen.h index 6ccfa7bd6c78..46e1ecb00c77 100644 --- a/connectd/connectd_wiregen.h +++ b/connectd/connectd_wiregen.h @@ -31,6 +31,8 @@ enum connectd_wire { WIRE_CONNECTD_PEER_CONNECTED = 2002, /* master -> connectd: peer has disconnected. */ WIRE_CONNECTD_PEER_DISCONNECTED = 2015, + /* master -> connectd: give message to peer and disconnect. Three fds: peer */ + WIRE_CONNECTD_PEER_FINAL_MSG = 2003, /* master -> connectd: do you have a memleak? */ WIRE_CONNECTD_DEV_MEMLEAK = 2033, WIRE_CONNECTD_DEV_MEMLEAK_REPLY = 2133, @@ -92,6 +94,11 @@ bool fromwire_connectd_peer_connected(const tal_t *ctx, const void *p, struct no u8 *towire_connectd_peer_disconnected(const tal_t *ctx, const struct node_id *id); bool fromwire_connectd_peer_disconnected(const void *p, struct node_id *id); +/* WIRE: CONNECTD_PEER_FINAL_MSG */ +/* master -> connectd: give message to peer and disconnect. Three fds: peer */ +u8 *towire_connectd_peer_final_msg(const tal_t *ctx, const struct node_id *id, const struct per_peer_state *pps, const u8 *msg); +bool fromwire_connectd_peer_final_msg(const tal_t *ctx, const void *p, struct node_id *id, struct per_peer_state **pps, u8 **msg); + /* WIRE: CONNECTD_DEV_MEMLEAK */ /* master -> connectd: do you have a memleak? */ u8 *towire_connectd_dev_memleak(const tal_t *ctx); @@ -103,4 +110,4 @@ bool fromwire_connectd_dev_memleak_reply(const void *p, bool *leak); #endif /* LIGHTNING_CONNECTD_CONNECTD_WIREGEN_H */ -// SHA256STAMP:9bbb0b97a226bd5c85a21bafde42c7fd438b8107d6d30b7c7b17c16a6cbd3557 +// SHA256STAMP:7c9585941825eab65d734eb1c233eee5c78b5792e19ec68f0a9986abca2b0ffe diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index f0bec05a9b20..9d143c96891b 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -315,6 +315,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_CONNECT_TO_PEER: case WIRE_CONNECTD_PEER_DISCONNECTED: case WIRE_CONNECTD_DEV_MEMLEAK: + case WIRE_CONNECTD_PEER_FINAL_MSG: /* This is a reply, so never gets through to here. */ case WIRE_CONNECTD_INIT_REPLY: case WIRE_CONNECTD_ACTIVATE_REPLY: From e2f225e4cdb6884464b5885692b50b5f1cd7f882 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jun 2021 12:52:21 +0930 Subject: [PATCH 41/62] lightningd: use connectd to send the final error message, not openingd/dualopend Signed-off-by: Rusty Russell --- connectd/connectd.c | 11 ++++++++- gossipd/test/run-bench-find_route.c | 3 --- gossipd/test/run-find_route-specific.c | 3 --- gossipd/test/run-find_route.c | 3 --- gossipd/test/run-overlong.c | 3 --- lightningd/peer_control.c | 27 +++++++++++++++------ lightningd/test/run-invoice-select-inchan.c | 6 +++++ wallet/db_postgres_sqlgen.c | 2 +- wallet/db_sqlite3_sqlgen.c | 2 +- wallet/statements_gettextgen.po | 6 ++--- wallet/test/run-wallet.c | 6 +++++ 11 files changed, 47 insertions(+), 25 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index f92f2e187cd7..3aba28ddd88a 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1654,6 +1654,7 @@ static struct io_plan *peer_final_msg(struct io_conn *conn, u8 *finalmsg; int fds[3]; + f->daemon = daemon; /* pps is allocated off f, so fds are closed when f freed. */ if (!fromwire_connectd_peer_final_msg(f, msg, &f->id, &pps, &finalmsg)) master_badmsg(WIRE_CONNECTD_PEER_FINAL_MSG, msg); @@ -1662,8 +1663,16 @@ static struct io_plan *peer_final_msg(struct io_conn *conn, tal_add_destructor(f, destroy_final_msg_data); /* Get the fds for this peer. */ - for (size_t i = 0; i < ARRAY_SIZE(fds); i++) + io_fd_block(io_conn_fd(conn), true); + for (size_t i = 0; i < ARRAY_SIZE(fds); i++) { fds[i] = fdpass_recv(io_conn_fd(conn)); + if (fds[i] == -1) + status_failed(STATUS_FAIL_MASTER_IO, + "Getting fd %zu after peer_final_msg: %s", + i, strerror(errno)); + } + io_fd_block(io_conn_fd(conn), false); + /* We put peer fd into conn, but pps needs to free the rest */ per_peer_state_set_fds(pps, -1, fds[1], fds[2]); diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index 476053582169..0772b7d2348f 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -92,9 +92,6 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* Generated stub for update_peers_broadcast_index */ -void update_peers_broadcast_index(struct list_head *peers UNNEEDED, u32 offset UNNEEDED) -{ fprintf(stderr, "update_peers_broadcast_index called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ #if DEVELOPER diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index 52256dacb368..64060092c2cb 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -78,9 +78,6 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* Generated stub for update_peers_broadcast_index */ -void update_peers_broadcast_index(struct list_head *peers UNNEEDED, u32 offset UNNEEDED) -{ fprintf(stderr, "update_peers_broadcast_index called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ #if DEVELOPER diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index 609282974484..41400bf2cd05 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -78,9 +78,6 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* Generated stub for update_peers_broadcast_index */ -void update_peers_broadcast_index(struct list_head *peers UNNEEDED, u32 offset UNNEEDED) -{ fprintf(stderr, "update_peers_broadcast_index called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ #if DEVELOPER diff --git a/gossipd/test/run-overlong.c b/gossipd/test/run-overlong.c index d400aa792626..2a16642f7026 100644 --- a/gossipd/test/run-overlong.c +++ b/gossipd/test/run-overlong.c @@ -78,9 +78,6 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_warningfmt called!\n"); abort(); } -/* Generated stub for update_peers_broadcast_index */ -void update_peers_broadcast_index(struct list_head *peers UNNEEDED, u32 offset UNNEEDED) -{ fprintf(stderr, "update_peers_broadcast_index called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ #if DEVELOPER diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index f1ddbf6cbbfe..e9cea141b449 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1136,10 +1136,6 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa notify_connect(ld, &peer->id, payload->incoming, &addr); - /* No err, all good. */ - error = NULL; - -send_error: if (feature_negotiated(ld->our_features, peer->their_features, OPT_DUAL_FUND)) { @@ -1150,11 +1146,28 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa || channel->state == AWAITING_UNILATERAL); channel->peer->addr = addr; channel->peer->connected_incoming = payload->incoming; - peer_restart_dualopend(peer, payload->pps, channel, error); + peer_restart_dualopend(peer, payload->pps, channel, NULL); } else - peer_start_dualopend(peer, payload->pps, error); + peer_start_dualopend(peer, payload->pps, NULL); } else - peer_start_openingd(peer, payload->pps, error); + peer_start_openingd(peer, payload->pps, NULL); + return; + +send_error: + log_debug(ld->log, "Telling connectd to send error %s", + tal_hex(tmpctx, error)); + /* Get connectd to send error and close. */ + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, &peer->id, + payload->pps, error))); + subd_send_fd(ld->connectd, payload->pps->peer_fd); + subd_send_fd(ld->connectd, payload->pps->gossip_fd); + subd_send_fd(ld->connectd, payload->pps->gossip_store_fd); + /* Don't close those fds! */ + payload->pps->peer_fd + = payload->pps->gossip_fd + = payload->pps->gossip_store_fd + = -1; } static bool diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 5634b25b9549..c3683f6df151 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -613,6 +613,9 @@ void subd_req_(const tal_t *ctx UNNEEDED, void (*replycb)(struct subd * UNNEEDED, const u8 * UNNEEDED, const int * UNNEEDED, void *) UNNEEDED, void *replycb_data UNNEEDED) { fprintf(stderr, "subd_req_ called!\n"); abort(); } +/* Generated stub for subd_send_fd */ +void subd_send_fd(struct subd *sd UNNEEDED, int fd UNNEEDED) +{ fprintf(stderr, "subd_send_fd called!\n"); abort(); } /* Generated stub for subd_send_msg */ void subd_send_msg(struct subd *sd UNNEEDED, const u8 *msg_out UNNEEDED) { fprintf(stderr, "subd_send_msg called!\n"); abort(); } @@ -637,6 +640,9 @@ u8 *towire_channeld_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_bas /* Generated stub for towire_connectd_connect_to_peer */ u8 *towire_connectd_connect_to_peer(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u32 seconds_waited UNNEEDED, const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "towire_connectd_connect_to_peer called!\n"); abort(); } +/* Generated stub for towire_connectd_peer_final_msg */ +u8 *towire_connectd_peer_final_msg(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct per_peer_state *pps UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } /* Generated stub for towire_dualopend_send_shutdown */ u8 *towire_dualopend_send_shutdown(const tal_t *ctx UNNEEDED, const u8 *shutdown_scriptpubkey UNNEEDED) { fprintf(stderr, "towire_dualopend_send_shutdown called!\n"); abort(); } diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index 9525c7814b4f..87548771d5a5 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:435d8c98449934c86167d11929b515312babce55bae5487dc3cdc201cb4ba0fe +// SHA256STAMP:c9d9d585e1fcba900b06f41123aa77c6fbd98945ffe485528c2601323f340bed diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index f6121dad17e7..0dfbe477b09b 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:435d8c98449934c86167d11929b515312babce55bae5487dc3cdc201cb4ba0fe +// SHA256STAMP:c9d9d585e1fcba900b06f41123aa77c6fbd98945ffe485528c2601323f340bed diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index afd5883f41c4..f0ce5888695e 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1250,11 +1250,11 @@ msgstr "" msgid "not a valid SQL statement" msgstr "" -#: wallet/test/run-wallet.c:1449 +#: wallet/test/run-wallet.c:1455 msgid "SELECT COUNT(1) FROM channel_funding_inflights WHERE channel_id = ?;" msgstr "" -#: wallet/test/run-wallet.c:1647 +#: wallet/test/run-wallet.c:1653 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:fdfbb1278ba9e09884c9205e54fc16bb9e66a0362a5aaad11a7efd42746d8e72 +# SHA256STAMP:ef55222dd765f18ae8d8f000c51b6024bbe7acafc17dd0b6837a180d7b736270 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 4401f4468fe1..1043a2a2a472 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -706,6 +706,9 @@ void subd_req_(const tal_t *ctx UNNEEDED, void (*replycb)(struct subd * UNNEEDED, const u8 * UNNEEDED, const int * UNNEEDED, void *) UNNEEDED, void *replycb_data UNNEEDED) { fprintf(stderr, "subd_req_ called!\n"); abort(); } +/* Generated stub for subd_send_fd */ +void subd_send_fd(struct subd *sd UNNEEDED, int fd UNNEEDED) +{ fprintf(stderr, "subd_send_fd called!\n"); abort(); } /* Generated stub for subd_send_msg */ void subd_send_msg(struct subd *sd UNNEEDED, const u8 *msg_out UNNEEDED) { fprintf(stderr, "subd_send_msg called!\n"); abort(); } @@ -760,6 +763,9 @@ u8 *towire_connectd_connect_to_peer(const tal_t *ctx UNNEEDED, const struct node /* Generated stub for towire_connectd_peer_disconnected */ u8 *towire_connectd_peer_disconnected(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_connectd_peer_disconnected called!\n"); abort(); } +/* Generated stub for towire_connectd_peer_final_msg */ +u8 *towire_connectd_peer_final_msg(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct per_peer_state *pps UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } /* Generated stub for towire_custommsg_out */ u8 *towire_custommsg_out(const tal_t *ctx UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "towire_custommsg_out called!\n"); abort(); } From bf0320a53e950c0fc881789a34169bc1e7fee143 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jun 2021 12:53:05 +0930 Subject: [PATCH 42/62] openingd/dualopend: remove send_msg functionality. Signed-off-by: Rusty Russell --- lightningd/dual_open_control.c | 20 +++++------- lightningd/dual_open_control.h | 7 ++--- lightningd/opening_control.c | 5 +-- lightningd/opening_control.h | 3 +- lightningd/peer_control.c | 8 ++--- lightningd/test/run-invoice-select-inchan.c | 10 ++---- openingd/dualopend.c | 34 ++------------------- openingd/dualopend_wire.csv | 6 ---- openingd/dualopend_wiregen.c | 30 +++--------------- openingd/dualopend_wiregen.h | 10 +++--- openingd/openingd.c | 27 +--------------- openingd/openingd_wire.csv | 3 -- openingd/openingd_wiregen.c | 16 ++-------- openingd/openingd_wiregen.h | 6 ++-- wallet/db_postgres_sqlgen.c | 2 +- wallet/db_sqlite3_sqlgen.c | 2 +- wallet/statements_gettextgen.po | 6 ++-- wallet/test/run-wallet.c | 10 ++---- 18 files changed, 46 insertions(+), 159 deletions(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 5155129eaa9e..8da97f282ad5 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -2813,8 +2813,7 @@ AUTODATA(json_command, &openchannel_abort_command); static void start_fresh_dualopend(struct peer *peer, struct per_peer_state *pps, - struct channel *channel, - const u8 *send_msg) + struct channel *channel) { int hsmfd; u32 max_to_self_delay; @@ -2867,16 +2866,14 @@ static void start_fresh_dualopend(struct peer *peer, min_effective_htlc_capacity, pps, &channel->local_basepoints, &channel->local_funding_pubkey, - channel->minimum_depth, - send_msg); + channel->minimum_depth); subd_send_msg(channel->owner, take(msg)); } void peer_restart_dualopend(struct peer *peer, struct per_peer_state *pps, - struct channel *channel, - const u8 *send_msg) + struct channel *channel) { u32 max_to_self_delay; struct amount_msat min_effective_htlc_capacity; @@ -2886,7 +2883,7 @@ void peer_restart_dualopend(struct peer *peer, u8 *msg; if (channel_unsaved(channel)) { - start_fresh_dualopend(peer, pps, channel, send_msg); + start_fresh_dualopend(peer, pps, channel); return; } hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->dbid, @@ -2960,16 +2957,13 @@ void peer_restart_dualopend(struct peer *peer, channel->remote_upfront_shutdown_script, inflight->remote_tx_sigs, channel->fee_states, - channel->channel_flags, - send_msg); + channel->channel_flags); subd_send_msg(channel->owner, take(msg)); } -void peer_start_dualopend(struct peer *peer, - struct per_peer_state *pps, - const u8 *send_msg) +void peer_start_dualopend(struct peer *peer, struct per_peer_state *pps) { struct channel *channel; @@ -2979,5 +2973,5 @@ void peer_start_dualopend(struct peer *peer, peer->ld->config.fee_base, peer->ld->config.fee_per_satoshi); - start_fresh_dualopend(peer, pps, channel, send_msg); + start_fresh_dualopend(peer, pps, channel); } diff --git a/lightningd/dual_open_control.h b/lightningd/dual_open_control.h index 4166d3ec6ecd..8657a40a6b1b 100644 --- a/lightningd/dual_open_control.h +++ b/lightningd/dual_open_control.h @@ -6,14 +6,11 @@ struct per_peer_state; -void peer_start_dualopend(struct peer *peer, - struct per_peer_state *pps, - const u8 *send_msg); +void peer_start_dualopend(struct peer *peer, struct per_peer_state *pps); void peer_restart_dualopend(struct peer *peer, struct per_peer_state *pps, - struct channel *channel, - const u8 *send_msg); + struct channel *channel); void dualopen_tell_depth(struct subd *dualopend, struct channel *channel, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index df6336c1f156..684c472b154e 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -876,9 +876,7 @@ static unsigned int openingd_msg(struct subd *openingd, return 0; } -void peer_start_openingd(struct peer *peer, - struct per_peer_state *pps, - const u8 *send_msg) +void peer_start_openingd(struct peer *peer, struct per_peer_state *pps) { int hsmfd; u32 max_to_self_delay; @@ -944,7 +942,6 @@ void peer_start_openingd(struct peer *peer, feature_negotiated(peer->ld->our_features, peer->their_features, OPT_ANCHOR_OUTPUTS), - send_msg, IFDEV(peer->ld->dev_force_tmp_channel_id, NULL), IFDEV(peer->ld->dev_fast_gossip, false)); subd_send_msg(uc->open_daemon, take(msg)); diff --git a/lightningd/opening_control.h b/lightningd/opening_control.h index d3f0d7257c69..3c5f3343afc1 100644 --- a/lightningd/opening_control.h +++ b/lightningd/opening_control.h @@ -15,8 +15,7 @@ void json_add_uncommitted_channel(struct json_stream *response, const struct uncommitted_channel *uc); void peer_start_openingd(struct peer *peer, - struct per_peer_state *pps, - const u8 *msg); + struct per_peer_state *pps); struct subd *peer_get_owning_subd(struct peer *peer); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e9cea141b449..812e3f12168d 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1113,7 +1113,7 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa assert(!channel->owner); channel->peer->addr = addr; channel->peer->connected_incoming = payload->incoming; - peer_restart_dualopend(peer, payload->pps, channel, NULL); + peer_restart_dualopend(peer, payload->pps, channel); return; case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: @@ -1146,11 +1146,11 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa || channel->state == AWAITING_UNILATERAL); channel->peer->addr = addr; channel->peer->connected_incoming = payload->incoming; - peer_restart_dualopend(peer, payload->pps, channel, NULL); + peer_restart_dualopend(peer, payload->pps, channel); } else - peer_start_dualopend(peer, payload->pps, NULL); + peer_start_dualopend(peer, payload->pps); } else - peer_start_openingd(peer, payload->pps, NULL); + peer_start_openingd(peer, payload->pps); return; send_error: diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index c3683f6df151..8709fb588644 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -569,8 +569,7 @@ struct channel *peer_normal_channel(struct peer *peer UNNEEDED) /* Generated stub for peer_restart_dualopend */ void peer_restart_dualopend(struct peer *peer UNNEEDED, struct per_peer_state *pps UNNEEDED, - struct channel *channel UNNEEDED, - const u8 *send_msg UNNEEDED) + struct channel *channel UNNEEDED) { fprintf(stderr, "peer_restart_dualopend called!\n"); abort(); } /* Generated stub for peer_start_channeld */ void peer_start_channeld(struct channel *channel UNNEEDED, @@ -585,14 +584,11 @@ void peer_start_closingd(struct channel *channel UNNEEDED, const u8 *channel_reestablish UNNEEDED) { fprintf(stderr, "peer_start_closingd called!\n"); abort(); } /* Generated stub for peer_start_dualopend */ -void peer_start_dualopend(struct peer *peer UNNEEDED, - struct per_peer_state *pps UNNEEDED, - const u8 *send_msg UNNEEDED) +void peer_start_dualopend(struct peer *peer UNNEEDED, struct per_peer_state *pps UNNEEDED) { fprintf(stderr, "peer_start_dualopend called!\n"); abort(); } /* Generated stub for peer_start_openingd */ void peer_start_openingd(struct peer *peer UNNEEDED, - struct per_peer_state *pps UNNEEDED, - const u8 *msg UNNEEDED) + struct per_peer_state *pps UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } /* Generated stub for peer_unsaved_channel */ struct channel *peer_unsaved_channel(struct peer *peer UNNEEDED) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index a264bbf208d4..5ebae60b291f 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3251,24 +3251,6 @@ static void do_reconnect_dance(struct state *state) peer_billboard(true, "Reconnected, and reestablished."); } -/*~ Is this message of type `error` with the special zero-id - * "fail-everything"? If lightningd asked us to send such a thing, we're - * done. */ -static void fail_if_all_error(const u8 *inner) -{ - struct channel_id channel_id; - u8 *data; - - if (!fromwire_error(tmpctx, inner, &channel_id, &data) - || !channel_id_is_all(&channel_id)) { - return; - } - - status_info("Master said send err: %s", - sanitize_error(tmpctx, inner, NULL)); - exit(0); -} - /* Standard lightningd-fd-is-ready-to-read demux code. Again, we could hang * here, but if we can't trust our parent, who can we trust? */ static u8 *handle_master_in(struct state *state) @@ -3456,7 +3438,7 @@ int main(int argc, char *argv[]) struct secret *none; struct fee_states *fee_states; enum side opener; - u8 *msg, *inner; + u8 *msg; struct amount_sat total_funding; struct amount_msat our_msat; @@ -3481,8 +3463,7 @@ int main(int argc, char *argv[]) &state->pps, &state->our_points, &state->our_funding_pubkey, - &state->minimum_depth, - &inner)) { + &state->minimum_depth)) { /*~ Initially we're not associated with a channel, but * handle_peer_gossip_or_error compares this. */ memset(&state->channel_id, 0, sizeof(state->channel_id)); @@ -3534,8 +3515,7 @@ int main(int argc, char *argv[]) &state->upfront_shutdown_script[REMOTE], &state->tx_state->remote_funding_sigs_rcvd, &fee_states, - &state->channel_flags, - &inner)) { + &state->channel_flags)) { /*~ We only reconnect on channels that the * saved the the database (exchanged commitment sigs) */ @@ -3571,14 +3551,6 @@ int main(int argc, char *argv[]) /* 3 == peer, 4 == gossipd, 5 = gossip_store, 6 = hsmd */ per_peer_state_set_fds(state->pps, 3, 4, 5); - /*~ If lightningd wanted us to send a msg, do so before we waste time - * doing work. If it's a global error, we'll close immediately. */ - if (inner != NULL) { - sync_crypto_write(state->pps, inner); - fail_if_all_error(inner); - tal_free(inner); - } - /*~ We need an initial per-commitment point whether we're funding or * they are, and lightningd has reserved a unique dbid for us already, * so we might as well get the hsm daemon to generate it now. */ diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 0608f57f60c8..217f935aa1a2 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -26,9 +26,6 @@ msgdata,dualopend_init,our_basepoints,basepoints, msgdata,dualopend_init,our_funding_pubkey,pubkey, # Constraints in case the other end tries to open a channel. msgdata,dualopend_init,minimum_depth,u32, -# Optional msg to send. -msgdata,dualopend_init,len,u16, -msgdata,dualopend_init,msg,u8,len # master-dualopend: peer has reconnected msgtype,dualopend_reinit,7001 @@ -67,9 +64,6 @@ msgdata,dualopend_reinit,remote_shutdown_scriptpubkey,u8,remote_shutdown_len msgdata,dualopend_reinit,remote_funding_sigs_received,bool, msgdata,dualopend_reinit,fee_states,fee_states, msgdata,dualopend_reinit,channel_flags,u8, -# Optional msg to send. -msgdata,dualopend_reinit,len,u16, -msgdata,dualopend_reinit,msg,u8,len # dualopend->master: they offered channel, should we continue? msgtype,dualopend_got_offer,7005 diff --git a/openingd/dualopend_wiregen.c b/openingd/dualopend_wiregen.c index e354eca37b0c..21ace02e4cb4 100644 --- a/openingd/dualopend_wiregen.c +++ b/openingd/dualopend_wiregen.c @@ -91,10 +91,9 @@ bool dualopend_wire_is_defined(u16 type) /* WIRE: DUALOPEND_INIT */ -u8 *towire_dualopend_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth, const u8 *msg) +u8 *towire_dualopend_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth) { u16 their_init_features_len = tal_count(their_init_features); - u16 len = tal_count(msg); u8 *p = tal_arr(ctx, u8, 0); towire_u16(&p, WIRE_DUALOPEND_INIT); @@ -113,16 +112,12 @@ u8 *towire_dualopend_init(const tal_t *ctx, const struct chainparams *chainparam towire_pubkey(&p, our_funding_pubkey); /* Constraints in case the other end tries to open a channel. */ towire_u32(&p, minimum_depth); - /* Optional msg to send. */ - towire_u16(&p, len); - towire_u8_array(&p, msg, len); return memcheck(p, tal_count(p)); } -bool fromwire_dualopend_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth, u8 **msg) +bool fromwire_dualopend_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth) { u16 their_init_features_len; - u16 len; const u8 *cursor = p; size_t plen = tal_count(p); @@ -146,22 +141,16 @@ bool fromwire_dualopend_init(const tal_t *ctx, const void *p, const struct chain fromwire_pubkey(&cursor, &plen, our_funding_pubkey); /* Constraints in case the other end tries to open a channel. */ *minimum_depth = fromwire_u32(&cursor, &plen); - /* Optional msg to send. */ - len = fromwire_u16(&cursor, &plen); - // 2nd case msg - *msg = len ? tal_arr(ctx, u8, len) : NULL; - fromwire_u8_array(&cursor, &plen, *msg, len); return cursor != NULL; } /* WIRE: DUALOPEND_REINIT */ /* master-dualopend: peer has reconnected */ -u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, const struct channel_config *their_config, const struct channel_id *channel_id, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct pubkey *their_funding_pubkey, u32 minimum_depth, const struct bitcoin_txid *funding_txid, u16 funding_txout, u32 orignal_feerate_per_kw_funding, u32 most_recent_feerate_per_kw_funding, struct amount_sat funding_satoshi, struct amount_msat our_funding, const struct basepoints *their_basepoints, const struct pubkey *remote_per_commit, const struct wally_psbt *funding_psbt, enum side opener, bool local_funding_locked, bool remote_funding_locked, bool send_shutdown, bool remote_shutdown_received, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, bool remote_funding_sigs_received, const struct fee_states *fee_states, u8 channel_flags, const u8 *msg) +u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, const struct channel_config *their_config, const struct channel_id *channel_id, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct pubkey *their_funding_pubkey, u32 minimum_depth, const struct bitcoin_txid *funding_txid, u16 funding_txout, u32 orignal_feerate_per_kw_funding, u32 most_recent_feerate_per_kw_funding, struct amount_sat funding_satoshi, struct amount_msat our_funding, const struct basepoints *their_basepoints, const struct pubkey *remote_per_commit, const struct wally_psbt *funding_psbt, enum side opener, bool local_funding_locked, bool remote_funding_locked, bool send_shutdown, bool remote_shutdown_received, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, bool remote_funding_sigs_received, const struct fee_states *fee_states, u8 channel_flags) { u16 their_init_features_len = tal_count(their_init_features); u16 local_shutdown_len = tal_count(local_shutdown_scriptpubkey); u16 remote_shutdown_len = tal_count(remote_shutdown_scriptpubkey); - u16 len = tal_count(msg); u8 *p = tal_arr(ctx, u8, 0); towire_u16(&p, WIRE_DUALOPEND_REINIT); @@ -200,18 +189,14 @@ u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainpar towire_bool(&p, remote_funding_sigs_received); towire_fee_states(&p, fee_states); towire_u8(&p, channel_flags); - /* Optional msg to send. */ - towire_u16(&p, len); - towire_u8_array(&p, msg, len); return memcheck(p, tal_count(p)); } -bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, struct channel_config *their_config, struct channel_id *channel_id, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct pubkey *their_funding_pubkey, u32 *minimum_depth, struct bitcoin_txid *funding_txid, u16 *funding_txout, u32 *orignal_feerate_per_kw_funding, u32 *most_recent_feerate_per_kw_funding, struct amount_sat *funding_satoshi, struct amount_msat *our_funding, struct basepoints *their_basepoints, struct pubkey *remote_per_commit, struct wally_psbt **funding_psbt, enum side *opener, bool *local_funding_locked, bool *remote_funding_locked, bool *send_shutdown, bool *remote_shutdown_received, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, bool *remote_funding_sigs_received, struct fee_states **fee_states, u8 *channel_flags, u8 **msg) +bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, struct channel_config *their_config, struct channel_id *channel_id, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct pubkey *their_funding_pubkey, u32 *minimum_depth, struct bitcoin_txid *funding_txid, u16 *funding_txout, u32 *orignal_feerate_per_kw_funding, u32 *most_recent_feerate_per_kw_funding, struct amount_sat *funding_satoshi, struct amount_msat *our_funding, struct basepoints *their_basepoints, struct pubkey *remote_per_commit, struct wally_psbt **funding_psbt, enum side *opener, bool *local_funding_locked, bool *remote_funding_locked, bool *send_shutdown, bool *remote_shutdown_received, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, bool *remote_funding_sigs_received, struct fee_states **fee_states, u8 *channel_flags) { u16 their_init_features_len; u16 local_shutdown_len; u16 remote_shutdown_len; - u16 len; const u8 *cursor = p; size_t plen = tal_count(p); @@ -259,11 +244,6 @@ bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct cha *remote_funding_sigs_received = fromwire_bool(&cursor, &plen); *fee_states = fromwire_fee_states(ctx, &cursor, &plen); *channel_flags = fromwire_u8(&cursor, &plen); - /* Optional msg to send. */ - len = fromwire_u16(&cursor, &plen); - // 2nd case msg - *msg = len ? tal_arr(ctx, u8, len) : NULL; - fromwire_u8_array(&cursor, &plen, *msg, len); return cursor != NULL; } @@ -932,4 +912,4 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:b93c5d3aad8cc2f256ed1205341ff68ea34d5bfc4a0d05071a8fe28177186bc5 +// SHA256STAMP:0cbaf66a07e1ffa2e01a85398b6937391af66eb78302e22fe7b9a3076963db4e diff --git a/openingd/dualopend_wiregen.h b/openingd/dualopend_wiregen.h index f1de1c45f48e..ac4c0597a1f2 100644 --- a/openingd/dualopend_wiregen.h +++ b/openingd/dualopend_wiregen.h @@ -86,13 +86,13 @@ bool dualopend_wire_is_defined(u16 type); /* WIRE: DUALOPEND_INIT */ -u8 *towire_dualopend_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth, const u8 *msg); -bool fromwire_dualopend_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth, u8 **msg); +u8 *towire_dualopend_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth); +bool fromwire_dualopend_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth); /* WIRE: DUALOPEND_REINIT */ /* master-dualopend: peer has reconnected */ -u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, const struct channel_config *their_config, const struct channel_id *channel_id, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct pubkey *their_funding_pubkey, u32 minimum_depth, const struct bitcoin_txid *funding_txid, u16 funding_txout, u32 orignal_feerate_per_kw_funding, u32 most_recent_feerate_per_kw_funding, struct amount_sat funding_satoshi, struct amount_msat our_funding, const struct basepoints *their_basepoints, const struct pubkey *remote_per_commit, const struct wally_psbt *funding_psbt, enum side opener, bool local_funding_locked, bool remote_funding_locked, bool send_shutdown, bool remote_shutdown_received, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, bool remote_funding_sigs_received, const struct fee_states *fee_states, u8 channel_flags, const u8 *msg); -bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, struct channel_config *their_config, struct channel_id *channel_id, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct pubkey *their_funding_pubkey, u32 *minimum_depth, struct bitcoin_txid *funding_txid, u16 *funding_txout, u32 *orignal_feerate_per_kw_funding, u32 *most_recent_feerate_per_kw_funding, struct amount_sat *funding_satoshi, struct amount_msat *our_funding, struct basepoints *their_basepoints, struct pubkey *remote_per_commit, struct wally_psbt **funding_psbt, enum side *opener, bool *local_funding_locked, bool *remote_funding_locked, bool *send_shutdown, bool *remote_shutdown_received, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, bool *remote_funding_sigs_received, struct fee_states **fee_states, u8 *channel_flags, u8 **msg); +u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, const struct channel_config *their_config, const struct channel_id *channel_id, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct pubkey *their_funding_pubkey, u32 minimum_depth, const struct bitcoin_txid *funding_txid, u16 funding_txout, u32 orignal_feerate_per_kw_funding, u32 most_recent_feerate_per_kw_funding, struct amount_sat funding_satoshi, struct amount_msat our_funding, const struct basepoints *their_basepoints, const struct pubkey *remote_per_commit, const struct wally_psbt *funding_psbt, enum side opener, bool local_funding_locked, bool remote_funding_locked, bool send_shutdown, bool remote_shutdown_received, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, bool remote_funding_sigs_received, const struct fee_states *fee_states, u8 channel_flags); +bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, struct channel_config *their_config, struct channel_id *channel_id, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct pubkey *their_funding_pubkey, u32 *minimum_depth, struct bitcoin_txid *funding_txid, u16 *funding_txout, u32 *orignal_feerate_per_kw_funding, u32 *most_recent_feerate_per_kw_funding, struct amount_sat *funding_satoshi, struct amount_msat *our_funding, struct basepoints *their_basepoints, struct pubkey *remote_per_commit, struct wally_psbt **funding_psbt, enum side *opener, bool *local_funding_locked, bool *remote_funding_locked, bool *send_shutdown, bool *remote_shutdown_received, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, bool *remote_funding_sigs_received, struct fee_states **fee_states, u8 *channel_flags); /* WIRE: DUALOPEND_GOT_OFFER */ /* dualopend->master: they offered channel */ @@ -216,4 +216,4 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak); #endif /* LIGHTNING_OPENINGD_DUALOPEND_WIREGEN_H */ -// SHA256STAMP:b93c5d3aad8cc2f256ed1205341ff68ea34d5bfc4a0d05071a8fe28177186bc5 +// SHA256STAMP:0cbaf66a07e1ffa2e01a85398b6937391af66eb78302e22fe7b9a3076963db4e diff --git a/openingd/openingd.c b/openingd/openingd.c index 1e55e834ea12..7a0d1bdeed2d 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1197,22 +1197,6 @@ static void handle_gossip_in(struct state *state) handle_gossip_msg(state->pps, take(msg)); } -/*~ Is this message of a `warning` or `error`? If lightningd asked us to send - * such a thing, it wants to close the connection. */ -static void fail_if_warning_or_error(const u8 *inner) -{ - struct channel_id channel_id; - u8 *data; - - if (!fromwire_warning(tmpctx, inner, &channel_id, &data) - && !fromwire_error(tmpctx, inner, &channel_id, &data)) - return; - - status_info("Master said send %s", - sanitize_error(tmpctx, inner, NULL)); - exit(0); -} - /* Memory leak detection is DEVELOPER-only because we go to great lengths to * record the backtrace when allocations occur: without that, the leak * detection tends to be useless for diagnosing where the leak came from, but @@ -1336,7 +1320,7 @@ int main(int argc, char *argv[]) { setup_locale(); - u8 *msg, *inner; + u8 *msg; struct pollfd pollfd[3]; struct state *state = tal(NULL, struct state); struct secret *none; @@ -1364,7 +1348,6 @@ int main(int argc, char *argv[]) &state->their_features, &state->option_static_remotekey, &state->option_anchor_outputs, - &inner, &force_tmp_channel_id, &dev_fast_gossip)) master_badmsg(WIRE_OPENINGD_INIT, msg); @@ -1376,14 +1359,6 @@ int main(int argc, char *argv[]) /* 3 == peer, 4 == gossipd, 5 = gossip_store, 6 = hsmd */ per_peer_state_set_fds(state->pps, 3, 4, 5); - /*~ If lightningd wanted us to send a msg, do so before we waste time - * doing work. If it's a warning, we'll close immediately. */ - if (inner != NULL) { - sync_crypto_write(state->pps, inner); - fail_if_warning_or_error(inner); - tal_free(inner); - } - /*~ Initially we're not associated with a channel, but * handle_peer_gossip_or_error compares this. */ memset(&state->channel_id, 0, sizeof(state->channel_id)); diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index db52cdc609a4..0c4e6ff80b4c 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -26,9 +26,6 @@ msgdata,openingd_init,lfeatures_len,u16, msgdata,openingd_init,lfeatures,u8,lfeatures_len msgdata,openingd_init,option_static_remotekey,bool, msgdata,openingd_init,option_anchor_outputs,bool, -# Optional msg to send. -msgdata,openingd_init,len,u16, -msgdata,openingd_init,msg,u8,len msgdata,openingd_init,dev_temporary_channel_id,?byte,32 msgdata,openingd_init,dev_fast_gossip,bool, diff --git a/openingd/openingd_wiregen.c b/openingd/openingd_wiregen.c index 8ac045def396..28442fda3cea 100644 --- a/openingd/openingd_wiregen.c +++ b/openingd/openingd_wiregen.c @@ -63,10 +63,9 @@ bool openingd_wire_is_defined(u16 type) /* WIRE: OPENINGD_INIT */ -u8 *towire_openingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth, u32 min_feerate, u32 max_feerate, const u8 *lfeatures, bool option_static_remotekey, bool option_anchor_outputs, const u8 *msg, const struct channel_id *dev_temporary_channel_id, bool dev_fast_gossip) +u8 *towire_openingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth, u32 min_feerate, u32 max_feerate, const u8 *lfeatures, bool option_static_remotekey, bool option_anchor_outputs, const struct channel_id *dev_temporary_channel_id, bool dev_fast_gossip) { u16 lfeatures_len = tal_count(lfeatures); - u16 len = tal_count(msg); u8 *p = tal_arr(ctx, u8, 0); towire_u16(&p, WIRE_OPENINGD_INIT); @@ -89,9 +88,6 @@ u8 *towire_openingd_init(const tal_t *ctx, const struct chainparams *chainparams towire_u8_array(&p, lfeatures, lfeatures_len); towire_bool(&p, option_static_remotekey); towire_bool(&p, option_anchor_outputs); - /* Optional msg to send. */ - towire_u16(&p, len); - towire_u8_array(&p, msg, len); if (!dev_temporary_channel_id) towire_bool(&p, false); else { @@ -102,10 +98,9 @@ u8 *towire_openingd_init(const tal_t *ctx, const struct chainparams *chainparams return memcheck(p, tal_count(p)); } -bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth, u32 *min_feerate, u32 *max_feerate, u8 **lfeatures, bool *option_static_remotekey, bool *option_anchor_outputs, u8 **msg, struct channel_id **dev_temporary_channel_id, bool *dev_fast_gossip) +bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth, u32 *min_feerate, u32 *max_feerate, u8 **lfeatures, bool *option_static_remotekey, bool *option_anchor_outputs, struct channel_id **dev_temporary_channel_id, bool *dev_fast_gossip) { u16 lfeatures_len; - u16 len; const u8 *cursor = p; size_t plen = tal_count(p); @@ -133,11 +128,6 @@ bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainp fromwire_u8_array(&cursor, &plen, *lfeatures, lfeatures_len); *option_static_remotekey = fromwire_bool(&cursor, &plen); *option_anchor_outputs = fromwire_bool(&cursor, &plen); - /* Optional msg to send. */ - len = fromwire_u16(&cursor, &plen); - // 2nd case msg - *msg = len ? tal_arr(ctx, u8, len) : NULL; - fromwire_u8_array(&cursor, &plen, *msg, len); if (!fromwire_bool(&cursor, &plen)) *dev_temporary_channel_id = NULL; else { @@ -579,4 +569,4 @@ bool fromwire_openingd_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:056c30b94922859252e3b4171c02c1986242c0ec1f71f78ea62c3a0b5d26a696 +// SHA256STAMP:edd7ee392dff0ddd0dff3a383692ba852a403e64e43290dba5dece69ae438e61 diff --git a/openingd/openingd_wiregen.h b/openingd/openingd_wiregen.h index 9fdf7f45e5f0..ba64e5567272 100644 --- a/openingd/openingd_wiregen.h +++ b/openingd/openingd_wiregen.h @@ -58,8 +58,8 @@ bool openingd_wire_is_defined(u16 type); /* WIRE: OPENINGD_INIT */ -u8 *towire_openingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth, u32 min_feerate, u32 max_feerate, const u8 *lfeatures, bool option_static_remotekey, bool option_anchor_outputs, const u8 *msg, const struct channel_id *dev_temporary_channel_id, bool dev_fast_gossip); -bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth, u32 *min_feerate, u32 *max_feerate, u8 **lfeatures, bool *option_static_remotekey, bool *option_anchor_outputs, u8 **msg, struct channel_id **dev_temporary_channel_id, bool *dev_fast_gossip); +u8 *towire_openingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth, u32 min_feerate, u32 max_feerate, const u8 *lfeatures, bool option_static_remotekey, bool option_anchor_outputs, const struct channel_id *dev_temporary_channel_id, bool dev_fast_gossip); +bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth, u32 *min_feerate, u32 *max_feerate, u8 **lfeatures, bool *option_static_remotekey, bool *option_anchor_outputs, struct channel_id **dev_temporary_channel_id, bool *dev_fast_gossip); /* WIRE: OPENINGD_GOT_OFFER */ /* Openingd->master: they offered channel */ @@ -121,4 +121,4 @@ bool fromwire_openingd_dev_memleak_reply(const void *p, bool *leak); #endif /* LIGHTNING_OPENINGD_OPENINGD_WIREGEN_H */ -// SHA256STAMP:056c30b94922859252e3b4171c02c1986242c0ec1f71f78ea62c3a0b5d26a696 +// SHA256STAMP:edd7ee392dff0ddd0dff3a383692ba852a403e64e43290dba5dece69ae438e61 diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index 87548771d5a5..1e4899c67f3c 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:c9d9d585e1fcba900b06f41123aa77c6fbd98945ffe485528c2601323f340bed +// SHA256STAMP:2839b3ea02654d43cce04742850e4c42541818c1641ab5119f077d859a288e5a diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 0dfbe477b09b..79582f04f224 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:c9d9d585e1fcba900b06f41123aa77c6fbd98945ffe485528c2601323f340bed +// SHA256STAMP:2839b3ea02654d43cce04742850e4c42541818c1641ab5119f077d859a288e5a diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index f0ce5888695e..f41b5e86ab1b 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1250,11 +1250,11 @@ msgstr "" msgid "not a valid SQL statement" msgstr "" -#: wallet/test/run-wallet.c:1455 +#: wallet/test/run-wallet.c:1451 msgid "SELECT COUNT(1) FROM channel_funding_inflights WHERE channel_id = ?;" msgstr "" -#: wallet/test/run-wallet.c:1653 +#: wallet/test/run-wallet.c:1649 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:ef55222dd765f18ae8d8f000c51b6024bbe7acafc17dd0b6837a180d7b736270 +# SHA256STAMP:61244f420c5eefe9cf60f0599cdd6c17d38f719ed2bc5acac93ee1109f121dcf diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 1043a2a2a472..e93ebe418c85 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -641,8 +641,7 @@ void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDE /* Generated stub for peer_restart_dualopend */ void peer_restart_dualopend(struct peer *peer UNNEEDED, struct per_peer_state *pps UNNEEDED, - struct channel *channel UNNEEDED, - const u8 *send_msg UNNEEDED) + struct channel *channel UNNEEDED) { fprintf(stderr, "peer_restart_dualopend called!\n"); abort(); } /* Generated stub for peer_start_channeld */ void peer_start_channeld(struct channel *channel UNNEEDED, @@ -657,14 +656,11 @@ void peer_start_closingd(struct channel *channel UNNEEDED, const u8 *channel_reestablish UNNEEDED) { fprintf(stderr, "peer_start_closingd called!\n"); abort(); } /* Generated stub for peer_start_dualopend */ -void peer_start_dualopend(struct peer *peer UNNEEDED, - struct per_peer_state *pps UNNEEDED, - const u8 *send_msg UNNEEDED) +void peer_start_dualopend(struct peer *peer UNNEEDED, struct per_peer_state *pps UNNEEDED) { fprintf(stderr, "peer_start_dualopend called!\n"); abort(); } /* Generated stub for peer_start_openingd */ void peer_start_openingd(struct peer *peer UNNEEDED, - struct per_peer_state *pps UNNEEDED, - const u8 *msg UNNEEDED) + struct per_peer_state *pps UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } /* Generated stub for peer_wire_is_defined */ bool peer_wire_is_defined(u16 type UNNEEDED) From e619bf00fbf5df8752da76e460c798e758402fef Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 28 May 2021 13:23:28 +0930 Subject: [PATCH 43/62] pytest: show problem with pay when not enough HTLCs available. As you can see, I did a lot of debugging before realizing that the actual problem is in the pay plugin :( Signed-off-by: Rusty Russell --- channeld/full_channel.c | 45 ++++++++++++++++++++++++++++------------- tests/test_pay.py | 12 ++++++++++- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 55ae6214c0b3..7001712f4f3a 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -178,18 +178,21 @@ void dump_htlcs(const struct channel *channel, const char *prefix) * committed: HTLCs currently committed. * pending_removal: HTLCs pending removal (subset of committed) * pending_addition: HTLCs pending addition (no overlap with committed) + * + * Also returns number of HTLCs for other side. */ -static void gather_htlcs(const tal_t *ctx, - const struct channel *channel, - enum side side, - const struct htlc ***committed, - const struct htlc ***pending_removal, - const struct htlc ***pending_addition) +static size_t gather_htlcs(const tal_t *ctx, + const struct channel *channel, + enum side side, + const struct htlc ***committed, + const struct htlc ***pending_removal, + const struct htlc ***pending_addition) { struct htlc_map_iter it; const struct htlc *htlc; const int committed_flag = HTLC_FLAG(side, HTLC_F_COMMITTED); const int pending_flag = HTLC_FLAG(side, HTLC_F_PENDING); + size_t num_other_side = 0; *committed = tal_arr(ctx, const struct htlc *, 0); if (pending_removal) @@ -198,18 +201,33 @@ static void gather_htlcs(const tal_t *ctx, *pending_addition = tal_arr(ctx, const struct htlc *, 0); if (!channel->htlcs) - return; + return num_other_side; for (htlc = htlc_map_first(channel->htlcs, &it); htlc; htlc = htlc_map_next(channel->htlcs, &it)) { if (htlc_has(htlc, committed_flag)) { +#ifdef SUPERVERBOSE + dump_htlc(htlc, "COMMITTED"); +#endif htlc_arr_append(committed, htlc); - if (htlc_has(htlc, pending_flag)) + if (htlc_has(htlc, pending_flag)) { +#ifdef SUPERVERBOSE + dump_htlc(htlc, "REMOVING"); +#endif htlc_arr_append(pending_removal, htlc); - } else if (htlc_has(htlc, pending_flag)) + } else if (htlc_owner(htlc) != side) + num_other_side++; + } else if (htlc_has(htlc, pending_flag)) { htlc_arr_append(pending_addition, htlc); +#ifdef SUPERVERBOSE + dump_htlc(htlc, "ADDING"); +#endif + if (htlc_owner(htlc) != side) + num_other_side++; + } } + return num_other_side; } static bool sum_offered_msatoshis(struct amount_msat *total, @@ -489,6 +507,7 @@ static enum channel_add_err add_htlc(struct channel *channel, enum side sender = htlc_state_owner(state), recipient = !sender; const struct htlc **committed, **adding, **removing; const struct channel_view *view; + size_t htlc_count; htlc = tal(tmpctx, struct htlc); @@ -563,7 +582,7 @@ static enum channel_add_err add_htlc(struct channel *channel, } /* Figure out what receiver will already be committed to. */ - gather_htlcs(tmpctx, channel, recipient, &committed, &removing, &adding); + htlc_count = gather_htlcs(tmpctx, channel, recipient, &committed, &removing, &adding); htlc_arr_append(&adding, htlc); /* BOLT #2: @@ -572,8 +591,7 @@ static enum channel_add_err add_htlc(struct channel *channel, * HTLCs to its local commitment transaction... * - SHOULD fail the channel. */ - if (tal_count(committed) - tal_count(removing) + tal_count(adding) - > channel->config[recipient].max_accepted_htlcs) { + if (htlc_count + 1 > channel->config[recipient].max_accepted_htlcs) { return CHANNEL_ERR_TOO_MANY_HTLCS; } @@ -583,8 +601,7 @@ static enum channel_add_err add_htlc(struct channel *channel, * spike with large commitment transactions. */ if (sender == LOCAL - && tal_count(committed) - tal_count(removing) + tal_count(adding) - > channel->config[LOCAL].max_accepted_htlcs) { + && htlc_count + 1 > channel->config[LOCAL].max_accepted_htlcs) { return CHANNEL_ERR_TOO_MANY_HTLCS; } diff --git a/tests/test_pay.py b/tests/test_pay.py index 5f75e0f0e02a..f49ac5250571 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4,7 +4,7 @@ from flaky import flaky # noqa: F401 from pyln.client import RpcError, Millisatoshi from pyln.proto.onion import TlvPayload -from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND +from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT from utils import ( DEVELOPER, wait_for, only_one, sync_blockheight, TIMEOUT, EXPERIMENTAL_FEATURES, env, VALGRIND @@ -4292,3 +4292,13 @@ def test_routehint_tous(node_factory, bitcoind): l3.stop() with pytest.raises(RpcError, match=r'Destination .* is not reachable directly and all routehints were unusable'): l2.rpc.pay(inv) + + +@pytest.mark.xfail(strict=True) +def test_pay_low_max_htlcs(node_factory): + """Test we can pay if *any* HTLC slots are available""" + + l1, l2, l3 = node_factory.line_graph(3, + opts={'max-concurrent-htlcs': 1}, + wait_for_announce=True) + l1.rpc.pay(l3.rpc.invoice(FUNDAMOUNT * 50, "test", "test")['bolt11']) From 05a3be15a3a479e3f5f40ed36eefea5664fd40ba Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 28 May 2021 11:53:41 +0200 Subject: [PATCH 44/62] pay: Fix use-after-free issue with routehints in shortlived payments This was triggered by having some part being started after the overall command already gave up, cleaning up the `cmd` context from which the routehints were allocated. The early exit of the command, as a result from a terminal state does not guarantee that no later attempt will try to find a route, especially if the attempt was started before we knew that it is doomed. --- plugins/pay.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index b08c55c64213..95d09ba84bf3 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -2026,7 +2026,7 @@ static struct command_result *json_paymod(struct command *cmd, if (!bolt12_has_prefix(b11str)) { b11 = - bolt11_decode(cmd, b11str, plugin_feature_set(cmd->plugin), + bolt11_decode(p, b11str, plugin_feature_set(cmd->plugin), NULL, chainparams, &b11_fail); if (b11 == NULL) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, @@ -2054,7 +2054,7 @@ static struct command_result *json_paymod(struct command *cmd, "Invalid bolt11:" " sets feature var_onion with no secret"); } else { - b12 = invoice_decode(cmd, b11str, strlen(b11str), + b12 = invoice_decode(p, b11str, strlen(b11str), plugin_feature_set(cmd->plugin), chainparams, &b12_fail); if (b12 == NULL) From d4def7e73aee4526480f2aab9ba4dd4dde1687f2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 28 May 2021 13:56:33 +0930 Subject: [PATCH 45/62] plugin/pay: prevent presplitter from dividing if max already tiny. Signed-off-by: Rusty Russell --- plugins/libplugin-pay.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 2c51847f383d..b0614824223c 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3446,7 +3446,7 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p) /* The presplitter only acts on the root and only in the first * step. */ size_t count = 0; - u32 htlcs = payment_max_htlcs(p) / PRESPLIT_MAX_HTLC_SHARE; + u32 htlcs; struct amount_msat target, amt = p->amount; char *partids = tal_strdup(tmpctx, ""); u64 target_amount = MPP_TARGET_SIZE; @@ -3471,6 +3471,11 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p) * but makes debugging a bit easier. */ root->next_partid++; + htlcs = payment_max_htlcs(p); + /* Divide it up if we can, but it might be v low already */ + if (htlcs >= PRESPLIT_MAX_HTLC_SHARE) + htlcs /= PRESPLIT_MAX_HTLC_SHARE; + if (htlcs == 0) { p->abort = true; return payment_fail( From 803c048a1f6514a1aa208a9fa1e8710cbf94a2b8 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 1 Jun 2021 16:00:35 +0200 Subject: [PATCH 46/62] pay: Compute the number of HTLCs needed after presplit correctly We were counting the attempts including the root payment, which resulted in an off-by-one error with the `test_pay_low_max_htlcs` test. Counting the children of the root payment after the presplitter had a go is the correct way to do it, since at that time we only have one level in the tree, no need to recurse and potentially count ourselves. --- plugins/libplugin-pay.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index b0614824223c..3ca2a253fb4a 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3596,18 +3596,17 @@ static void adaptive_splitter_cb(struct adaptive_split_mod_data *d, struct payme * update our htlc_budget that we own exclusively from now * on. We do this by subtracting the number of payment * attempts an eventual presplitter has already performed. */ - struct payment_tree_result res; - res = payment_collect_result(p); + int children = tal_count(p->children); d->htlc_budget = payment_max_htlcs(p); - if (res.attempts > d->htlc_budget) { + if (children > d->htlc_budget) { p->abort = true; return payment_fail( p, "Cannot add %d HTLCs to our channels, we " "only have %d HTLCs available.", - res.attempts, d->htlc_budget); + children, d->htlc_budget); } - d->htlc_budget -= res.attempts; + d->htlc_budget -= children; } if (p->step == PAYMENT_STEP_ONION_PAYLOAD) { From 5e1fadf7999dea895fba4b9cb8b909b1f66f6e82 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 2 Jun 2021 16:48:15 +0200 Subject: [PATCH 47/62] pay: Skip the presplitter mod if it'd exhaust our HTLC budget Changelog-Fixed: pay: The presplitter mod will no longer exhaust the HTLC budget. --- plugins/libplugin-pay.c | 12 +++++++++--- tests/test_pay.py | 4 +++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 3ca2a253fb4a..6769b4a7447a 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3476,14 +3476,20 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p) if (htlcs >= PRESPLIT_MAX_HTLC_SHARE) htlcs /= PRESPLIT_MAX_HTLC_SHARE; + int targethtlcs = + p->amount.millisatoshis / target_amount; /* Raw: division */ if (htlcs == 0) { p->abort = true; return payment_fail( p, "Cannot attempt payment, we have no channel to " "which we can add an HTLC"); - } else if (p->amount.millisatoshis / target_amount > htlcs) /* Raw: division */ - target = amount_msat_div(p->amount, htlcs); - else + } else if (targethtlcs > htlcs) { + paymod_log(p, LOG_INFORM, + "Number of pre-split HTLCs (%d) exceeds our " + "HTLC budget (%d), skipping pre-splitter", + targethtlcs, htlcs); + return payment_continue(p); + } else target = amount_msat(target_amount); /* If we are already below the target size don't split it diff --git a/tests/test_pay.py b/tests/test_pay.py index f49ac5250571..c701c38d4110 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4294,7 +4294,6 @@ def test_routehint_tous(node_factory, bitcoind): l2.rpc.pay(inv) -@pytest.mark.xfail(strict=True) def test_pay_low_max_htlcs(node_factory): """Test we can pay if *any* HTLC slots are available""" @@ -4302,3 +4301,6 @@ def test_pay_low_max_htlcs(node_factory): opts={'max-concurrent-htlcs': 1}, wait_for_announce=True) l1.rpc.pay(l3.rpc.invoice(FUNDAMOUNT * 50, "test", "test")['bolt11']) + l1.daemon.wait_for_log( + r'Number of pre-split HTLCs \([0-9]+\) exceeds our HTLC budget \([0-9]+\), skipping pre-splitter' + ) From b72c05fbda159e57634c99fe46710a41aced5d75 Mon Sep 17 00:00:00 2001 From: openoms Date: Tue, 1 Jun 2021 18:38:16 +0100 Subject: [PATCH 48/62] hsm_encryption: read from STDIN if not in a TTY Changelog-Added: hsmtool: allow piped passwords --- common/hsm_encryption.c | 62 ++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index d33c0ff56ba6..41388843924d 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include char *hsm_secret_encryption_key(const char *pass, struct secret *key) { @@ -84,31 +86,41 @@ char *read_stdin_pass(char **reason) char *passwd = NULL; size_t passwd_size = 0; - /* Set a temporary term, same as current but with ECHO disabled. */ - if (tcgetattr(fileno(stdin), ¤t_term) != 0) { - *reason = "Could not get current terminal options."; - return NULL; - } - temp_term = current_term; - temp_term.c_lflag &= ~ECHO; - if (tcsetattr(fileno(stdin), TCSAFLUSH, &temp_term) != 0) { - *reason = "Could not disable pass echoing."; - return NULL; - } - - /* Read the password, do not take the newline character into account. */ - if (getline(&passwd, &passwd_size, stdin) < 0) { - *reason = "Could not read pass from stdin."; - return NULL; - } - if (passwd[strlen(passwd) - 1] == '\n') - passwd[strlen(passwd) - 1] = '\0'; - - /* Restore the original terminal */ - if (tcsetattr(fileno(stdin), TCSAFLUSH, ¤t_term) != 0) { - *reason = "Could not restore terminal options."; - free(passwd); - return NULL; + if (isatty(fileno(stdin))) { + /* Set a temporary term, same as current but with ECHO disabled. */ + if (tcgetattr(fileno(stdin), ¤t_term) != 0) { + *reason = "Could not get current terminal options."; + return NULL; + } + temp_term = current_term; + temp_term.c_lflag &= ~ECHO; + if (tcsetattr(fileno(stdin), TCSAFLUSH, &temp_term) != 0) { + *reason = "Could not disable pass echoing."; + return NULL; + } + + /* Read the password, do not take the newline character into account. */ + if (getline(&passwd, &passwd_size, stdin) < 0) { + *reason = "Could not read pass from stdin."; + return NULL; + } + if (passwd[strlen(passwd) - 1] == '\n') + passwd[strlen(passwd) - 1] = '\0'; + + /* Restore the original terminal */ + if (tcsetattr(fileno(stdin), TCSAFLUSH, ¤t_term) != 0) { + *reason = "Could not restore terminal options."; + free(passwd); + return NULL; + } + } else { + /* Read from stdin, do not take the newline character into account. */ + if (getline(&passwd, &passwd_size, stdin) < 0) { + *reason = "Could not read pass from stdin."; + return NULL; + } + if (passwd[strlen(passwd) - 1] == '\n') + passwd[strlen(passwd) - 1] = '\0'; } return passwd; From c2e22344f72865dce13aefb55b1a77a77f74d466 Mon Sep 17 00:00:00 2001 From: openoms Date: Tue, 1 Jun 2021 19:25:20 +0100 Subject: [PATCH 49/62] lightningd: remove duplicate temp term creation --- lightningd/options.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lightningd/options.c b/lightningd/options.c index 6801d73f75be..e5d2cc1e812c 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -390,16 +390,8 @@ static char *opt_important_plugin(const char *arg, struct lightningd *ld) */ static char *opt_set_hsm_password(struct lightningd *ld) { - struct termios current_term, temp_term; char *passwd, *passwd_confirmation, *err; - /* Get the password from stdin, but don't echo it. */ - if (tcgetattr(fileno(stdin), ¤t_term) != 0) - return "Could not get current terminal options."; - temp_term = current_term; - temp_term.c_lflag &= ~ECHO; - if (tcsetattr(fileno(stdin), TCSAFLUSH, &temp_term) != 0) - return "Could not disable password echoing."; printf("The hsm_secret is encrypted with a password. In order to " "decrypt it and start the node you must provide the password.\n"); printf("Enter hsm_secret password:\n"); From b821de13ebd0507e16845d2420ddaaa61327105b Mon Sep 17 00:00:00 2001 From: openoms <43343391+openoms@users.noreply.github.com> Date: Thu, 3 Jun 2021 10:07:10 +0100 Subject: [PATCH 50/62] hsm_encryption.c: remove whitespace from line end --- common/hsm_encryption.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index 41388843924d..8909a0dd0db6 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include char *hsm_secret_encryption_key(const char *pass, struct secret *key) { From fab1ed6fca8fb52fdd8493ce355b308e65286e67 Mon Sep 17 00:00:00 2001 From: openoms <43343391+openoms@users.noreply.github.com> Date: Thu, 3 Jun 2021 11:45:03 +0100 Subject: [PATCH 51/62] hsm_encryption.c: fix source include order --- common/hsm_encryption.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index 8909a0dd0db6..2bc457a27443 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -1,9 +1,9 @@ #include #include #include +#include #include #include -#include char *hsm_secret_encryption_key(const char *pass, struct secret *key) { From 28953256d0083d5ab6a6f62cecc6eedc2a41b4f3 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Thu, 3 Jun 2021 22:18:10 -0300 Subject: [PATCH 52/62] stop requiring channel and direction on `sendonion` since they're not used. Changelog-Changed: `sendonion` no longer requires the gratuitous `direction` and `channel` fields in the `firsthop` parameter. --- doc/lightning-sendonion.7 | 3 +-- doc/lightning-sendonion.7.md | 2 -- lightningd/pay.c | 4 ++-- plugins/libplugin-pay.c | 2 -- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/doc/lightning-sendonion.7 b/doc/lightning-sendonion.7 index ab72fdd3155d..da3f8fe4ae75 100644 --- a/doc/lightning-sendonion.7 +++ b/doc/lightning-sendonion.7 @@ -39,7 +39,6 @@ to add an HTLC for 1002 millisatoshis and a delay of 21 blocks on top of the cur .RS { "id": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", - "direction": 1, "amount_msat": "1002msat", "delay": 21, } @@ -121,4 +120,4 @@ Christian Decker \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:19d7317d0c50409db6a09b1e4411fc9697976254be3555cbf3bcf2a28984529c +\" SHA256STAMP:1d7ceae27ac4ac51597b92de50cf7bc917c59bdf59f3280efc06491ed4efaffc diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index 486af15f4f67..29c26d9a1e46 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -37,7 +37,6 @@ to add an HTLC for 1002 millisatoshis and a delay of 21 blocks on top of the cur ```json { "id": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", - "direction": 1, "amount_msat": "1002msat", "delay": 21, } @@ -112,4 +111,3 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md - diff --git a/lightningd/pay.c b/lightningd/pay.c index 5a81280266e1..62910de22770 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1007,7 +1007,7 @@ send_payment_core(struct lightningd *ld, "peer found"); json_add_routefail_info(data, 0, WIRE_UNKNOWN_NEXT_PEER, - &ld->id, &first_hop->scid, + &ld->id, NULL, node_id_idx(&ld->id, &first_hop->node_id), NULL); @@ -1021,7 +1021,7 @@ send_payment_core(struct lightningd *ld, if (failmsg) { fail = immediate_routing_failure(cmd, ld, fromwire_peektype(failmsg), - &first_hop->scid, + channel->scid, &channel->peer->id); return sendpay_fail( diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 6769b4a7447a..474a46f6bd1b 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -1505,8 +1505,6 @@ static struct command_result *payment_createonion_success(struct command *cmd, json_add_hex_talarr(req->js, "onion", p->createonion_response->onion); json_object_start(req->js, "first_hop"); - json_add_short_channel_id(req->js, "channel", &first->scid); - json_add_num(req->js, "direction", first->direction); json_add_amount_msat_only(req->js, "amount_msat", first->amount); json_add_num(req->js, "delay", first->delay); json_add_node_id(req->js, "id", &first->node_id); From ebf90cdbecc668b412df55ec759ef1700062deff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 10:49:10 +0930 Subject: [PATCH 53/62] dev_disconnect: new option to stop using connection, but don't close. This allows us to ensure a packet is read by the other end, but we don't read anything else from them or write anything to them. Using '+' is similar, but because it closes the connection, the peer might notice before receiving the packet (such as if it does a write). Signed-off-by: Rusty Russell --- common/crypto_sync.c | 11 ++++++++--- common/dev_disconnect.c | 13 +++++++++---- common/dev_disconnect.h | 4 +++- connectd/peer_exchange_initmsg.c | 14 ++++++++++++-- devtools/gossipwith.c | 2 +- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/common/crypto_sync.c b/common/crypto_sync.c index e8f5f83212b2..c529de687169 100644 --- a/common/crypto_sync.c +++ b/common/crypto_sync.c @@ -17,7 +17,7 @@ void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) { #if DEVELOPER - bool post_sabotage = false; + bool post_sabotage = false, post_close; int type = fromwire_peektype(msg); #endif u8 *enc; @@ -28,18 +28,23 @@ void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) #if DEVELOPER switch (dev_disconnect(type)) { case DEV_DISCONNECT_BEFORE: - dev_sabotage_fd(pps->peer_fd); + dev_sabotage_fd(pps->peer_fd, true); peer_failed_connection_lost(); case DEV_DISCONNECT_DROPPKT: enc = tal_free(enc); /* FALL THRU */ case DEV_DISCONNECT_AFTER: post_sabotage = true; + post_close = true; break; case DEV_DISCONNECT_BLACKHOLE: dev_blackhole_fd(pps->peer_fd); break; case DEV_DISCONNECT_NORMAL: break; + case DEV_DISCONNECT_DISABLE_AFTER: + post_sabotage = true; + post_close = false; + break; } #endif if (!write_all(pps->peer_fd, enc, tal_count(enc))) @@ -48,7 +53,7 @@ void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) #if DEVELOPER if (post_sabotage) - dev_sabotage_fd(pps->peer_fd); + dev_sabotage_fd(pps->peer_fd, post_close); #endif } diff --git a/common/dev_disconnect.c b/common/dev_disconnect.c index d55c7f029b3e..e5a1a6f96ec6 100644 --- a/common/dev_disconnect.c +++ b/common/dev_disconnect.c @@ -90,16 +90,13 @@ enum dev_disconnect dev_disconnect(int pkt_type) return dev_disconnect_line[0]; } -void dev_sabotage_fd(int fd) +void dev_sabotage_fd(int fd, bool close_fd) { int fds[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) err(1, "dev_sabotage_fd: creating socketpair"); - /* Close one. */ - close(fds[0]); - #if defined(TCP_NODELAY) /* On Linux, at least, this flushes. */ int opt = TCP_NODELAY; @@ -108,6 +105,14 @@ void dev_sabotage_fd(int fd) #else #error No TCP_NODELAY? #endif + + /* Move fd out the way if we don't want to close it. */ + if (!close_fd) + dup(fd); + else + /* Close other end of socket. */ + close(fds[0]); + /* Move other over to the fd we want to sabotage. */ dup2(fds[1], fd); close(fds[1]); diff --git a/common/dev_disconnect.h b/common/dev_disconnect.h index 8760f35ee305..3c0f4b8973d4 100644 --- a/common/dev_disconnect.h +++ b/common/dev_disconnect.h @@ -15,13 +15,15 @@ enum dev_disconnect { DEV_DISCONNECT_DROPPKT = '@', /* Swallow all writes from now on, and do no more reads. */ DEV_DISCONNECT_BLACKHOLE = '0', + /* Don't use connection after sending packet, but don't close. */ + DEV_DISCONNECT_DISABLE_AFTER = 'x', }; /* Force a close fd before or after a certain packet type */ enum dev_disconnect dev_disconnect(int pkt_type); /* Make next write on fd fail as if they'd disconnected. */ -void dev_sabotage_fd(int fd); +void dev_sabotage_fd(int fd, bool close_fd); /* No more data to arrive, what's written is swallowed. */ void dev_blackhole_fd(int fd); diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index f5e2dc24de19..f4aecacaf10a 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -132,7 +132,14 @@ static struct io_plan *read_init(struct io_conn *conn, struct peer *peer) static struct io_plan *peer_write_postclose(struct io_conn *conn, struct peer *peer) { - dev_sabotage_fd(io_conn_fd(conn)); + dev_sabotage_fd(io_conn_fd(conn), true); + return read_init(conn, peer); +} + +static struct io_plan *peer_write_post_sabotage(struct io_conn *conn, + struct peer *peer) +{ + dev_sabotage_fd(io_conn_fd(conn), false); return read_init(conn, peer); } #endif @@ -197,7 +204,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, #if DEVELOPER switch (dev_disconnect(WIRE_INIT)) { case DEV_DISCONNECT_BEFORE: - dev_sabotage_fd(io_conn_fd(conn)); + dev_sabotage_fd(io_conn_fd(conn), true); break; case DEV_DISCONNECT_DROPPKT: peer->msg = tal_free(peer->msg); /* FALL THRU */ @@ -209,6 +216,9 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, break; case DEV_DISCONNECT_NORMAL: break; + case DEV_DISCONNECT_DISABLE_AFTER: + next = peer_write_post_sabotage; + break; } #endif /* DEVELOPER */ diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 92983bf9261c..9f0a715f3c3a 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -67,7 +67,7 @@ void status_fmt(enum log_level level, } #if DEVELOPER -void dev_sabotage_fd(int fd) +void dev_sabotage_fd(int fd, bool close_fd) { abort(); } From f028576306539266de732d773385c962623d21c7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 10:50:10 +0930 Subject: [PATCH 54/62] pytest: test for closing needing retranmission bug. This was turned by at random by CI: 1. Alice has sent shutdown, but it still waiting for revoke_and_ack. 2. Bob has sent and received shutdown, and sent revoke_and_ack, so it considers it time for signature exchange. 3. Disconnect before Alice received revoke_and_ack. 4. Reconnect, Bob is in closingd, which doesn't rexmit revoke_and_ack. 5. Timeout. Signed-off-by: Rusty Russell --- tests/test_closing.py | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index cd95224c2036..29510d32b8aa 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2740,3 +2740,48 @@ def test_shutdown_alternate_txid(node_factory, bitcoind): wait_for(lambda: l2.rpc.listpeers()['peers'] == []) wait_for(lambda: l1.rpc.listpeers()['peers'] == []) + + +@pytest.mark.xfail(strict=True) +@pytest.mark.developer("needs dev_disconnect") +def test_htlc_rexmit_while_closing(node_factory, executor): + """Retranmitting an HTLC revocation while shutting down should work""" + # l1 disconnects after sending second COMMITMENT_SIGNED. + # Then it stops receiving after sending WIRE_SHUTDOWN (which is before it + # reads the revoke_and_ack). + disconnects = ['+WIRE_COMMITMENT_SIGNED*2', + 'xWIRE_SHUTDOWN'] + + l1, l2 = node_factory.line_graph(2, opts=[{'may_reconnect': True, + 'dev-no-reconnect': None, + 'disconnect': disconnects}, + {'may_reconnect': True, + 'dev-no-reconnect': None}]) + + # Start payment, will disconnect + l1.pay(l2, 200000) + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is False) + + # Tell it to close (will block) + fut = executor.submit(l1.rpc.close, l2.info['id']) + + # Original problem was with multiple disconnects, but to simplify we make + # l2 send shutdown too. + fut2 = executor.submit(l2.rpc.close, l1.info['id']) + + # Reconnect, shutdown will continue disconnect again + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + # Now l2 should be in CLOSINGD_SIGEXCHANGE, l1 still waiting on + # WIRE_REVOKE_AND_ACK. + wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE') + assert only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CHANNELD_SHUTTING_DOWN' + + # They don't realize they're not talking, so disconnect and reconnect. + l1.rpc.disconnect(l2.info['id'], force=True) + + # Now it hangs, since l1 is expecting rexmit of revoke-and-ack. + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + fut.result(TIMEOUT) + fut2.result(TIMEOUT) From 9ac82ab9d1478c0adbfb699e2f61703cd06dcee4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 10:51:10 +0930 Subject: [PATCH 55/62] lightningd: always pass closing connections through channeld. It handles all the cases of retransmission, and in the normal case retransmits shutdown and immediately returns for us to run closingd. This is actually far simpler and reduces code duplication. [ Includes fixup to stop warn_unused_result from Christian ] Signed-off-by: Rusty Russell Changelog-Fixed: Protocol: We could get stuck on signature exchange if we needed to retransmit the final revoke_and_ack. --- closingd/closingd.c | 154 -------------------- closingd/closingd_wire.csv | 7 - closingd/closingd_wiregen.c | 24 +-- closingd/closingd_wiregen.h | 6 +- common/dev_disconnect.c | 8 +- lightningd/channel_control.c | 18 ++- lightningd/closing_control.c | 34 +---- lightningd/closing_control.h | 7 +- lightningd/dual_open_control.c | 2 +- lightningd/peer_control.c | 8 +- lightningd/test/run-invoice-select-inchan.c | 6 - tests/test_closing.py | 1 - tests/test_connection.py | 2 +- wallet/db_postgres_sqlgen.c | 2 +- wallet/db_sqlite3_sqlgen.c | 2 +- wallet/statements_gettextgen.po | 6 +- wallet/test/run-wallet.c | 6 - 17 files changed, 33 insertions(+), 260 deletions(-) diff --git a/closingd/closingd.c b/closingd/closingd.c index 80c10adcaa87..6c95ab92679d 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -130,143 +130,6 @@ static u8 *closing_read_peer_msg(const tal_t *ctx, } } -static struct pubkey get_per_commitment_point(u64 commitment_number) -{ - u8 *msg; - struct pubkey commitment_point; - struct secret *s; - - /* Our current per-commitment point is the commitment point in the last - * received signed commitment; HSM gives us that and the previous - * secret (which we don't need). */ - msg = towire_hsmd_get_per_commitment_point(NULL, - commitment_number); - if (!wire_sync_write(HSM_FD, take(msg))) - status_failed(STATUS_FAIL_HSM_IO, - "Writing get_per_commitment_point to HSM: %s", - strerror(errno)); - - msg = wire_sync_read(tmpctx, HSM_FD); - if (!msg) - status_failed(STATUS_FAIL_HSM_IO, - "Reading resp get_per_commitment_point reply: %s", - strerror(errno)); - if (!fromwire_hsmd_get_per_commitment_point_reply(tmpctx, msg, - &commitment_point, - &s)) - status_failed(STATUS_FAIL_HSM_IO, - "Bad per_commitment_point reply %s", - tal_hex(tmpctx, msg)); - - return commitment_point; -} - -static void do_reconnect(struct per_peer_state *pps, - const struct channel_id *channel_id, - const u64 next_index[NUM_SIDES], - u64 revocations_received, - const u8 *channel_reestablish, - const u8 *final_scriptpubkey, - const struct secret *last_remote_per_commit_secret, - const struct bitcoin_outpoint *wrong_funding) -{ - u8 *msg; - struct channel_id their_channel_id; - u64 next_local_commitment_number, next_remote_revocation_number; - struct pubkey my_current_per_commitment_point, next_commitment_point; - struct secret their_secret; - struct tlv_shutdown_tlvs *tlvs; - - my_current_per_commitment_point = get_per_commitment_point(next_index[LOCAL]-1); - - /* BOLT #2: - * - * - upon reconnection: - * - if a channel is in an error state: - * - SHOULD retransmit the error packet and ignore any other packets for - * that channel. - * - otherwise: - * - MUST transmit `channel_reestablish` for each channel. - * - MUST wait to receive the other node's `channel_reestablish` - * message before sending any other messages for that channel. - * - * The sending node: - * - MUST set `next_commitment_number` to the commitment number - * of the next `commitment_signed` it expects to receive. - * - MUST set `next_revocation_number` to the commitment number - * of the next `revoke_and_ack` message it expects to receive. - */ - - msg = towire_channel_reestablish(NULL, channel_id, - next_index[LOCAL], - revocations_received, - last_remote_per_commit_secret, - &my_current_per_commitment_point); - sync_crypto_write(pps, take(msg)); - - /* They might have already sent reestablish, which triggered us */ - if (!channel_reestablish) { - do { - tal_free(channel_reestablish); - channel_reestablish = closing_read_peer_msg(tmpctx, pps, - channel_id); - /* They *should* send reestablish first, but lnd - * sends other messages, which we can ignore since - * we're closing anyway... */ - } while (fromwire_peektype(channel_reestablish) - != WIRE_CHANNEL_REESTABLISH); - } - - if (!fromwire_channel_reestablish(channel_reestablish, &their_channel_id, - &next_local_commitment_number, - &next_remote_revocation_number, - &their_secret, - &next_commitment_point)) { - peer_failed_warn(pps, channel_id, - "bad reestablish msg: %s %s", - peer_wire_name(fromwire_peektype(channel_reestablish)), - tal_hex(tmpctx, channel_reestablish)); - } - status_debug("Got reestablish commit=%"PRIu64" revoke=%"PRIu64, - next_local_commitment_number, - next_remote_revocation_number); - - /* BOLT #2: - * - * A node: - *... - * - upon reconnection: - * - if it has sent a previous `shutdown`: - * - MUST retransmit `shutdown`. - */ - if (wrong_funding) { - tlvs = tlv_shutdown_tlvs_new(tmpctx); - tlvs->wrong_funding - = tal(tlvs, struct tlv_shutdown_tlvs_wrong_funding); - tlvs->wrong_funding->txid = wrong_funding->txid; - tlvs->wrong_funding->outnum = wrong_funding->n; - } else - tlvs = NULL; - - msg = towire_shutdown(NULL, channel_id, final_scriptpubkey, tlvs); - sync_crypto_write(pps, take(msg)); - - /* BOLT #2: - * - * A node: - *... - * - if `next_commitment_number` is 1 in both the `channel_reestablish` it sent and received: - * - MUST retransmit `funding_locked`. - */ - if (next_index[REMOTE] == 1 && next_index[LOCAL] == 1) { - status_debug("Retransmitting funding_locked for channel %s", - type_to_string(tmpctx, struct channel_id, channel_id)); - next_commitment_point = get_per_commitment_point(next_index[LOCAL]); - msg = towire_funding_locked(NULL, channel_id, &next_commitment_point); - sync_crypto_write(pps, take(msg)); - } -} - static void send_offer(struct per_peer_state *pps, const struct chainparams *chainparams, const struct channel_id *channel_id, @@ -648,11 +511,7 @@ int main(int argc, char *argv[]) u8 fee_negotiation_step_unit; char fee_negotiation_step_str[32]; /* fee_negotiation_step + "sat" */ struct channel_id channel_id; - bool reconnected; - u64 next_index[NUM_SIDES], revocations_received; enum side whose_turn; - u8 *channel_reestablish; - struct secret last_remote_per_commit_secret; struct bitcoin_outpoint *wrong_funding; subdaemon_setup(argc, argv); @@ -678,12 +537,6 @@ int main(int argc, char *argv[]) &scriptpubkey[REMOTE], &fee_negotiation_step, &fee_negotiation_step_unit, - &reconnected, - &next_index[LOCAL], - &next_index[REMOTE], - &revocations_received, - &channel_reestablish, - &last_remote_per_commit_secret, &dev_fast_gossip, &wrong_funding)) master_badmsg(WIRE_CLOSINGD_INIT, msg); @@ -716,13 +569,6 @@ int main(int argc, char *argv[]) &funding_pubkey[LOCAL], &funding_pubkey[REMOTE]); - if (reconnected) - do_reconnect(pps, &channel_id, - next_index, revocations_received, - channel_reestablish, scriptpubkey[LOCAL], - &last_remote_per_commit_secret, - wrong_funding); - peer_billboard( true, "Negotiating closing fee between %s and %s satoshi (ideal %s) " diff --git a/closingd/closingd_wire.csv b/closingd/closingd_wire.csv index c6f7ea07f5f3..4558c9a982d2 100644 --- a/closingd/closingd_wire.csv +++ b/closingd/closingd_wire.csv @@ -26,13 +26,6 @@ msgdata,closingd_init,remote_scriptpubkey_len,u16, msgdata,closingd_init,remote_scriptpubkey,u8,remote_scriptpubkey_len msgdata,closingd_init,fee_negotiation_step,u64, msgdata,closingd_init,fee_negotiation_step_unit,u8, -msgdata,closingd_init,reconnected,bool, -msgdata,closingd_init,next_index_local,u64, -msgdata,closingd_init,next_index_remote,u64, -msgdata,closingd_init,revocations_received,u64, -msgdata,closingd_init,channel_reestablish_len,u16, -msgdata,closingd_init,channel_reestablish,u8,channel_reestablish_len -msgdata,closingd_init,last_remote_secret,secret, msgdata,closingd_init,dev_fast_gossip,bool, msgdata,closingd_init,shutdown_wrong_funding,?bitcoin_outpoint, diff --git a/closingd/closingd_wiregen.c b/closingd/closingd_wiregen.c index 91f2693f80ca..94914f2ed452 100644 --- a/closingd/closingd_wiregen.c +++ b/closingd/closingd_wiregen.c @@ -48,11 +48,10 @@ bool closingd_wire_is_defined(u16 type) /* WIRE: CLOSINGD_INIT */ /* Begin! (passes peer fd */ -u8 *towire_closingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct per_peer_state *pps, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, const struct pubkey *local_fundingkey, const struct pubkey *remote_fundingkey, enum side opener, struct amount_sat local_sat, struct amount_sat remote_sat, struct amount_sat our_dust_limit, struct amount_sat min_fee_satoshi, struct amount_sat fee_limit_satoshi, struct amount_sat initial_fee_satoshi, const u8 *local_scriptpubkey, const u8 *remote_scriptpubkey, u64 fee_negotiation_step, u8 fee_negotiation_step_unit, bool reconnected, u64 next_index_local, u64 next_index_remote, u64 revocations_received, const u8 *channel_reestablish, const struct secret *last_remote_secret, bool dev_fast_gossip, const struct bitcoin_outpoint *shutdown_wrong_funding) +u8 *towire_closingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct per_peer_state *pps, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, const struct pubkey *local_fundingkey, const struct pubkey *remote_fundingkey, enum side opener, struct amount_sat local_sat, struct amount_sat remote_sat, struct amount_sat our_dust_limit, struct amount_sat min_fee_satoshi, struct amount_sat fee_limit_satoshi, struct amount_sat initial_fee_satoshi, const u8 *local_scriptpubkey, const u8 *remote_scriptpubkey, u64 fee_negotiation_step, u8 fee_negotiation_step_unit, bool dev_fast_gossip, const struct bitcoin_outpoint *shutdown_wrong_funding) { u16 local_scriptpubkey_len = tal_count(local_scriptpubkey); u16 remote_scriptpubkey_len = tal_count(remote_scriptpubkey); - u16 channel_reestablish_len = tal_count(channel_reestablish); u8 *p = tal_arr(ctx, u8, 0); towire_u16(&p, WIRE_CLOSINGD_INIT); @@ -77,13 +76,6 @@ u8 *towire_closingd_init(const tal_t *ctx, const struct chainparams *chainparams towire_u8_array(&p, remote_scriptpubkey, remote_scriptpubkey_len); towire_u64(&p, fee_negotiation_step); towire_u8(&p, fee_negotiation_step_unit); - towire_bool(&p, reconnected); - towire_u64(&p, next_index_local); - towire_u64(&p, next_index_remote); - towire_u64(&p, revocations_received); - towire_u16(&p, channel_reestablish_len); - towire_u8_array(&p, channel_reestablish, channel_reestablish_len); - towire_secret(&p, last_remote_secret); towire_bool(&p, dev_fast_gossip); if (!shutdown_wrong_funding) towire_bool(&p, false); @@ -94,11 +86,10 @@ u8 *towire_closingd_init(const tal_t *ctx, const struct chainparams *chainparams return memcheck(p, tal_count(p)); } -bool fromwire_closingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct per_peer_state **pps, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, struct pubkey *local_fundingkey, struct pubkey *remote_fundingkey, enum side *opener, struct amount_sat *local_sat, struct amount_sat *remote_sat, struct amount_sat *our_dust_limit, struct amount_sat *min_fee_satoshi, struct amount_sat *fee_limit_satoshi, struct amount_sat *initial_fee_satoshi, u8 **local_scriptpubkey, u8 **remote_scriptpubkey, u64 *fee_negotiation_step, u8 *fee_negotiation_step_unit, bool *reconnected, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u8 **channel_reestablish, struct secret *last_remote_secret, bool *dev_fast_gossip, struct bitcoin_outpoint **shutdown_wrong_funding) +bool fromwire_closingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct per_peer_state **pps, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, struct pubkey *local_fundingkey, struct pubkey *remote_fundingkey, enum side *opener, struct amount_sat *local_sat, struct amount_sat *remote_sat, struct amount_sat *our_dust_limit, struct amount_sat *min_fee_satoshi, struct amount_sat *fee_limit_satoshi, struct amount_sat *initial_fee_satoshi, u8 **local_scriptpubkey, u8 **remote_scriptpubkey, u64 *fee_negotiation_step, u8 *fee_negotiation_step_unit, bool *dev_fast_gossip, struct bitcoin_outpoint **shutdown_wrong_funding) { u16 local_scriptpubkey_len; u16 remote_scriptpubkey_len; - u16 channel_reestablish_len; const u8 *cursor = p; size_t plen = tal_count(p); @@ -130,15 +121,6 @@ bool fromwire_closingd_init(const tal_t *ctx, const void *p, const struct chainp fromwire_u8_array(&cursor, &plen, *remote_scriptpubkey, remote_scriptpubkey_len); *fee_negotiation_step = fromwire_u64(&cursor, &plen); *fee_negotiation_step_unit = fromwire_u8(&cursor, &plen); - *reconnected = fromwire_bool(&cursor, &plen); - *next_index_local = fromwire_u64(&cursor, &plen); - *next_index_remote = fromwire_u64(&cursor, &plen); - *revocations_received = fromwire_u64(&cursor, &plen); - channel_reestablish_len = fromwire_u16(&cursor, &plen); - // 2nd case channel_reestablish - *channel_reestablish = channel_reestablish_len ? tal_arr(ctx, u8, channel_reestablish_len) : NULL; - fromwire_u8_array(&cursor, &plen, *channel_reestablish, channel_reestablish_len); - fromwire_secret(&cursor, &plen, last_remote_secret); *dev_fast_gossip = fromwire_bool(&cursor, &plen); if (!fromwire_bool(&cursor, &plen)) *shutdown_wrong_funding = NULL; @@ -213,4 +195,4 @@ bool fromwire_closingd_complete(const void *p) return false; return cursor != NULL; } -// SHA256STAMP:95043321951ace6f7a5ee9f1dc0ae57c8a6427644b8b79d3e08e521dc9b3b49f +// SHA256STAMP:1dcbd7b74aeab22bc03fc0af061676d79772b025f85f58e8cc747e6f4d1df917 diff --git a/closingd/closingd_wiregen.h b/closingd/closingd_wiregen.h index ea2aa4f2e144..9b9895a23fcd 100644 --- a/closingd/closingd_wiregen.h +++ b/closingd/closingd_wiregen.h @@ -37,8 +37,8 @@ bool closingd_wire_is_defined(u16 type); /* WIRE: CLOSINGD_INIT */ /* Begin! (passes peer fd */ -u8 *towire_closingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct per_peer_state *pps, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, const struct pubkey *local_fundingkey, const struct pubkey *remote_fundingkey, enum side opener, struct amount_sat local_sat, struct amount_sat remote_sat, struct amount_sat our_dust_limit, struct amount_sat min_fee_satoshi, struct amount_sat fee_limit_satoshi, struct amount_sat initial_fee_satoshi, const u8 *local_scriptpubkey, const u8 *remote_scriptpubkey, u64 fee_negotiation_step, u8 fee_negotiation_step_unit, bool reconnected, u64 next_index_local, u64 next_index_remote, u64 revocations_received, const u8 *channel_reestablish, const struct secret *last_remote_secret, bool dev_fast_gossip, const struct bitcoin_outpoint *shutdown_wrong_funding); -bool fromwire_closingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct per_peer_state **pps, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, struct pubkey *local_fundingkey, struct pubkey *remote_fundingkey, enum side *opener, struct amount_sat *local_sat, struct amount_sat *remote_sat, struct amount_sat *our_dust_limit, struct amount_sat *min_fee_satoshi, struct amount_sat *fee_limit_satoshi, struct amount_sat *initial_fee_satoshi, u8 **local_scriptpubkey, u8 **remote_scriptpubkey, u64 *fee_negotiation_step, u8 *fee_negotiation_step_unit, bool *reconnected, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u8 **channel_reestablish, struct secret *last_remote_secret, bool *dev_fast_gossip, struct bitcoin_outpoint **shutdown_wrong_funding); +u8 *towire_closingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct per_peer_state *pps, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, const struct pubkey *local_fundingkey, const struct pubkey *remote_fundingkey, enum side opener, struct amount_sat local_sat, struct amount_sat remote_sat, struct amount_sat our_dust_limit, struct amount_sat min_fee_satoshi, struct amount_sat fee_limit_satoshi, struct amount_sat initial_fee_satoshi, const u8 *local_scriptpubkey, const u8 *remote_scriptpubkey, u64 fee_negotiation_step, u8 fee_negotiation_step_unit, bool dev_fast_gossip, const struct bitcoin_outpoint *shutdown_wrong_funding); +bool fromwire_closingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct per_peer_state **pps, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, struct pubkey *local_fundingkey, struct pubkey *remote_fundingkey, enum side *opener, struct amount_sat *local_sat, struct amount_sat *remote_sat, struct amount_sat *our_dust_limit, struct amount_sat *min_fee_satoshi, struct amount_sat *fee_limit_satoshi, struct amount_sat *initial_fee_satoshi, u8 **local_scriptpubkey, u8 **remote_scriptpubkey, u64 *fee_negotiation_step, u8 *fee_negotiation_step_unit, bool *dev_fast_gossip, struct bitcoin_outpoint **shutdown_wrong_funding); /* WIRE: CLOSINGD_RECEIVED_SIGNATURE */ /* We received an offer */ @@ -56,4 +56,4 @@ bool fromwire_closingd_complete(const void *p); #endif /* LIGHTNING_CLOSINGD_CLOSINGD_WIREGEN_H */ -// SHA256STAMP:95043321951ace6f7a5ee9f1dc0ae57c8a6427644b8b79d3e08e521dc9b3b49f +// SHA256STAMP:1dcbd7b74aeab22bc03fc0af061676d79772b025f85f58e8cc747e6f4d1df917 diff --git a/common/dev_disconnect.c b/common/dev_disconnect.c index e5a1a6f96ec6..ab8302a29964 100644 --- a/common/dev_disconnect.c +++ b/common/dev_disconnect.c @@ -107,9 +107,11 @@ void dev_sabotage_fd(int fd, bool close_fd) #endif /* Move fd out the way if we don't want to close it. */ - if (!close_fd) - dup(fd); - else + if (!close_fd) { + if (dup(fd) == -1) { + ; /* -Wunused-result */ + } + } else /* Close other end of socket. */ close(fds[0]); diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index bf9f91cf52ff..6d2a54caeda8 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -321,12 +321,15 @@ static void peer_start_closingd_after_shutdown(struct channel *channel, per_peer_state_set_fds_arr(pps, fds); /* This sets channel->owner, closes down channeld. */ - peer_start_closingd(channel, pps, false, NULL); - channel_set_state(channel, - CHANNELD_SHUTTING_DOWN, - CLOSINGD_SIGEXCHANGE, - REASON_UNKNOWN, - "Start closingd"); + peer_start_closingd(channel, pps); + + /* We might have reconnected, so already be here. */ + if (channel->state != CLOSINGD_SIGEXCHANGE) + channel_set_state(channel, + CHANNELD_SHUTTING_DOWN, + CLOSINGD_SIGEXCHANGE, + REASON_UNKNOWN, + "Start closingd"); } static void forget(struct channel *channel) @@ -595,7 +598,8 @@ void peer_start_channeld(struct channel *channel, channel->remote_funding_locked, &scid, reconnected, - channel->state == CHANNELD_SHUTTING_DOWN, + channel->state == CHANNELD_SHUTTING_DOWN + || channel->state == CLOSINGD_SIGEXCHANGE, channel->shutdown_scriptpubkey[REMOTE] != NULL, channel->shutdown_scriptpubkey[LOCAL], channel->channel_flags, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index a4769cdf81df..0729ee76d3b4 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -193,17 +193,13 @@ static unsigned closing_msg(struct subd *sd, const u8 *msg, const int *fds UNUSE } void peer_start_closingd(struct channel *channel, - struct per_peer_state *pps, - bool reconnected, - const u8 *channel_reestablish) + struct per_peer_state *pps) { u8 *initmsg; u32 feerate; struct amount_sat minfee, startfee, feelimit; - u64 num_revocations; struct amount_msat their_msat; int hsmfd; - struct secret last_remote_per_commit_secret; struct lightningd *ld = channel->peer->ld; u32 final_commit_feerate; @@ -270,9 +266,6 @@ void peer_start_closingd(struct channel *channel, if (amount_sat_greater(minfee, feelimit)) minfee = feelimit; - num_revocations - = revocations_received(&channel->their_shachain.chain); - /* BOLT #3: * * Each node offering a signature: @@ -292,25 +285,6 @@ void peer_start_closingd(struct channel *channel, return; } - /* BOLT #2: - * - if `next_revocation_number` equals 0: - * - MUST set `your_last_per_commitment_secret` to all zeroes - * - otherwise: - * - MUST set `your_last_per_commitment_secret` to the last - * `per_commitment_secret` it received - */ - if (num_revocations == 0) - memset(&last_remote_per_commit_secret, 0, - sizeof(last_remote_per_commit_secret)); - else if (!shachain_get_secret(&channel->their_shachain.chain, - num_revocations-1, - &last_remote_per_commit_secret)) { - channel_fail_permanent(channel, - REASON_LOCAL, - "Could not get revocation secret %"PRIu64, - num_revocations-1); - return; - } initmsg = towire_closingd_init(tmpctx, chainparams, pps, @@ -329,12 +303,6 @@ void peer_start_closingd(struct channel *channel, channel->shutdown_scriptpubkey[REMOTE], channel->closing_fee_negotiation_step, channel->closing_fee_negotiation_step_unit, - reconnected, - channel->next_index[LOCAL], - channel->next_index[REMOTE], - num_revocations, - channel_reestablish, - &last_remote_per_commit_secret, IFDEV(ld->dev_fast_gossip, false), channel->shutdown_wrong_funding); diff --git a/lightningd/closing_control.h b/lightningd/closing_control.h index cbaa76ab9941..0ee6afb29a56 100644 --- a/lightningd/closing_control.h +++ b/lightningd/closing_control.h @@ -3,13 +3,10 @@ #include "config.h" #include -struct channel_id; -struct crypto_state; +struct channel; struct per_peer_state; void peer_start_closingd(struct channel *channel, - struct per_peer_state *pps, - bool reconnected, - const u8 *channel_reestablish); + struct per_peer_state *pps); #endif /* LIGHTNING_LIGHTNINGD_CLOSING_CONTROL_H */ diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 8da97f282ad5..83f178c24db7 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1291,7 +1291,7 @@ static void handle_channel_closed(struct subd *dualopend, per_peer_state_set_fds_arr(pps, fds); - peer_start_closingd(channel, pps, false, NULL); + peer_start_closingd(channel, pps); channel_set_state(channel, CHANNELD_SHUTTING_DOWN, CLOSINGD_SIGEXCHANGE, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 812e3f12168d..5b019668e756 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1118,17 +1118,11 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: case CHANNELD_SHUTTING_DOWN: - assert(!channel->owner); - channel->peer->addr = addr; - channel->peer->connected_incoming = payload->incoming; - peer_start_channeld(channel, payload->pps, NULL, true); - return; - case CLOSINGD_SIGEXCHANGE: assert(!channel->owner); channel->peer->addr = addr; channel->peer->connected_incoming = payload->incoming; - peer_start_closingd(channel, payload->pps, true, NULL); + peer_start_channeld(channel, payload->pps, NULL, true); return; } abort(); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 8709fb588644..c9fc3d5355fb 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -577,12 +577,6 @@ void peer_start_channeld(struct channel *channel UNNEEDED, const u8 *fwd_msg UNNEEDED, bool reconnected UNNEEDED) { fprintf(stderr, "peer_start_channeld called!\n"); abort(); } -/* Generated stub for peer_start_closingd */ -void peer_start_closingd(struct channel *channel UNNEEDED, - struct per_peer_state *pps UNNEEDED, - bool reconnected UNNEEDED, - const u8 *channel_reestablish UNNEEDED) -{ fprintf(stderr, "peer_start_closingd called!\n"); abort(); } /* Generated stub for peer_start_dualopend */ void peer_start_dualopend(struct peer *peer UNNEEDED, struct per_peer_state *pps UNNEEDED) { fprintf(stderr, "peer_start_dualopend called!\n"); abort(); } diff --git a/tests/test_closing.py b/tests/test_closing.py index 29510d32b8aa..c50e67e91c59 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2742,7 +2742,6 @@ def test_shutdown_alternate_txid(node_factory, bitcoind): wait_for(lambda: l1.rpc.listpeers()['peers'] == []) -@pytest.mark.xfail(strict=True) @pytest.mark.developer("needs dev_disconnect") def test_htlc_rexmit_while_closing(node_factory, executor): """Retranmitting an HTLC revocation while shutting down should work""" diff --git a/tests/test_connection.py b/tests/test_connection.py index e2ede44dfe8a..eb7d3d61e156 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -588,7 +588,7 @@ def test_reconnect_no_update(node_factory, executor, bitcoind): # Close will trigger the @WIRE_SHUTDOWN and we then wait for the # automatic reconnection to trigger the retransmission. l1.rpc.close(l2.info['id'], 0) - l2.daemon.wait_for_log(r"closingd.* Retransmitting funding_locked for channel") + l2.daemon.wait_for_log(r"channeld.* Retransmitting funding_locked for channel") l1.daemon.wait_for_log(r"CLOSINGD_COMPLETE") diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index 1e4899c67f3c..0532e5341813 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:2839b3ea02654d43cce04742850e4c42541818c1641ab5119f077d859a288e5a +// SHA256STAMP:44990f6dd9d83577ced85310f3b32ae7bb2dd2d21a4fdc08ac65596f23012aaa diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 79582f04f224..aa0127355682 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:2839b3ea02654d43cce04742850e4c42541818c1641ab5119f077d859a288e5a +// SHA256STAMP:44990f6dd9d83577ced85310f3b32ae7bb2dd2d21a4fdc08ac65596f23012aaa diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index f41b5e86ab1b..68e6b4381381 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1250,11 +1250,11 @@ msgstr "" msgid "not a valid SQL statement" msgstr "" -#: wallet/test/run-wallet.c:1451 +#: wallet/test/run-wallet.c:1445 msgid "SELECT COUNT(1) FROM channel_funding_inflights WHERE channel_id = ?;" msgstr "" -#: wallet/test/run-wallet.c:1649 +#: wallet/test/run-wallet.c:1643 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:61244f420c5eefe9cf60f0599cdd6c17d38f719ed2bc5acac93ee1109f121dcf +# SHA256STAMP:57b906c6eff2852af492ca63454e88e65d9350017c9458f9f31bd7c2665cd8a8 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index e93ebe418c85..df63d6f12289 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -649,12 +649,6 @@ void peer_start_channeld(struct channel *channel UNNEEDED, const u8 *fwd_msg UNNEEDED, bool reconnected UNNEEDED) { fprintf(stderr, "peer_start_channeld called!\n"); abort(); } -/* Generated stub for peer_start_closingd */ -void peer_start_closingd(struct channel *channel UNNEEDED, - struct per_peer_state *pps UNNEEDED, - bool reconnected UNNEEDED, - const u8 *channel_reestablish UNNEEDED) -{ fprintf(stderr, "peer_start_closingd called!\n"); abort(); } /* Generated stub for peer_start_dualopend */ void peer_start_dualopend(struct peer *peer UNNEEDED, struct per_peer_state *pps UNNEEDED) { fprintf(stderr, "peer_start_dualopend called!\n"); abort(); } From 3f0446e29ca7771537ba5b17653dfab0f3173b30 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 10:52:10 +0930 Subject: [PATCH 56/62] pytest: add test (failing) which allows talking about closed channels. In particular, if one side sees the final CLOSING_SIGNED and the other doesn't, we won't talk when it reconnects. Signed-off-by: Rusty Russell --- tests/test_closing.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index c50e67e91c59..2219c82ada2a 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2784,3 +2784,29 @@ def test_htlc_rexmit_while_closing(node_factory, executor): fut.result(TIMEOUT) fut2.result(TIMEOUT) + + +@pytest.mark.xfail(strict=True) +@pytest.mark.openchannel('v1') +@pytest.mark.developer("needs dev_disconnect") +def test_you_forgot_closed_channel(node_factory, executor): + """Ideally you'd keep talking to us about closed channels""" + disconnects = ['@WIRE_CLOSING_SIGNED'] + + l1, l2 = node_factory.line_graph(2, opts=[{'may_reconnect': True, + 'dev-no-reconnect': None}, + {'may_reconnect': True, + 'dev-no-reconnect': None, + 'disconnect': disconnects}]) + + l1.pay(l2, 200000) + + fut = executor.submit(l1.rpc.close, l2.info['id']) + + # l2 considers the closing done, l1 does not + wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_COMPLETE') + assert only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE' + + # l1 reconnects, it should succeed. + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + fut.result(TIMEOUT) From 59fafad7c93c7caa09fce0fa4e8db8abb6447c53 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 10:53:10 +0930 Subject: [PATCH 57/62] common/read_peer_msg: don't try to handle reestablish/reopen. Let the callers do that (only channeld needs to do this). Signed-off-by: Rusty Russell --- channeld/channeld.c | 9 +++++++++ common/read_peer_msg.c | 13 ------------- common/read_peer_msg.h | 4 ++-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index d33e9da1e2fe..5dcccc5669ca 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2565,6 +2565,15 @@ static void peer_reconnect(struct peer *peer, tal_hex(msg, msg)); } + if (!channel_id_eq(&channel_id, &peer->channel_id)) { + peer_failed_err(peer->pps, + &channel_id, + "bad reestablish msg for unknown channel %s: %s", + type_to_string(tmpctx, struct channel_id, + &channel_id), + tal_hex(msg, msg)); + } + status_debug("Got reestablish commit=%"PRIu64" revoke=%"PRIu64, next_commitment_number, next_revocation_number); diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index 2215c10643f9..6859be7cd073 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -162,7 +162,6 @@ bool handle_peer_gossip_or_error(struct per_peer_state *pps, { char *err; bool warning; - struct channel_id actual; #if DEVELOPER /* Any odd-typed unknown message is handled by the caller, so if we @@ -200,18 +199,6 @@ bool handle_peer_gossip_or_error(struct per_peer_state *pps, goto handled; } - /* They're talking about a different channel? */ - if (is_wrong_channel(msg, channel_id, &actual)) { - status_debug("Rejecting %s for unknown channel_id %s", - peer_wire_name(fromwire_peektype(msg)), - type_to_string(tmpctx, struct channel_id, &actual)); - sync_crypto_write(pps, - take(towire_errorfmt(NULL, &actual, - "Multiple channels" - " unsupported"))); - goto handled; - } - return false; handled: diff --git a/common/read_peer_msg.h b/common/read_peer_msg.h index 824c2ba3d518..698a11976a93 100644 --- a/common/read_peer_msg.h +++ b/common/read_peer_msg.h @@ -62,8 +62,8 @@ bool is_wrong_channel(const u8 *msg, const struct channel_id *expected, * @msg: the peer message (only taken if returns true). * * This returns true if it handled the packet: a gossip packet (forwarded - * to gossipd), an error packet (causes peer_failed_received_errmsg or - * ignored), or a message about the wrong channel (sends sync error reply). + * to gossipd), or an error packet (causes peer_failed_received_errmsg or + * ignored). */ bool handle_peer_gossip_or_error(struct per_peer_state *pps, const struct channel_id *channel_id, From 13440dfdd005f864073af128282cd99dbaf31fe4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 14:47:57 +0930 Subject: [PATCH 58/62] lightningd: add channel_closed helper, expose find_channel_by_id(). Signed-off-by: Rusty Russell --- lightningd/channel.h | 13 +++++++++++++ lightningd/channel_control.c | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lightningd/channel.h b/lightningd/channel.h index d04746450baf..eeaf8635467b 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -362,6 +362,10 @@ struct channel *any_channel_by_scid(struct lightningd *ld, struct channel *channel_by_cid(struct lightningd *ld, const struct channel_id *cid); +/* Find this channel within peer */ +struct channel *find_channel_by_id(const struct peer *peer, + const struct channel_id *cid); + void channel_set_last_tx(struct channel *channel, struct bitcoin_tx *tx, const struct bitcoin_signature *sig, @@ -402,6 +406,15 @@ static inline bool channel_active(const struct channel *channel) && !channel_on_chain(channel); } +static inline bool channel_closed(const struct channel *channel) +{ + return channel->state == CLOSINGD_COMPLETE + || channel->state == AWAITING_UNILATERAL + || channel->state == FUNDING_SPEND_SEEN + || channel->state == ONCHAIN + || channel->state == CLOSED; +} + void get_channel_basepoints(struct lightningd *ld, const struct node_id *peer_id, const u64 dbid, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 6d2a54caeda8..79cd2b5ea709 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -764,8 +764,8 @@ void channel_notify_new_block(struct lightningd *ld, tal_free(to_forget); } -static struct channel *find_channel_by_id(const struct peer *peer, - const struct channel_id *cid) +struct channel *find_channel_by_id(const struct peer *peer, + const struct channel_id *cid) { struct channel *c; From 211db2b5a1d40f8f795687a8cd50032b01b0cc8d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 14:48:57 +0930 Subject: [PATCH 59/62] channeld: add a message to read if we only want to reestablish. This supports reestablish on a closed channel: we tell channeld to respond to the reestablish message appropriately, then close the channel. Signed-off-by: Rusty Russell --- channeld/channeld.c | 26 ++++++++++++++++++--- channeld/channeld_wire.csv | 2 ++ channeld/channeld_wiregen.c | 14 ++++++++--- channeld/channeld_wiregen.h | 6 ++--- lightningd/channel_control.c | 11 ++++++--- lightningd/channel_control.h | 3 ++- lightningd/dual_open_control.c | 2 +- lightningd/opening_control.c | 4 ++-- lightningd/peer_control.c | 3 ++- lightningd/test/run-invoice-select-inchan.c | 3 ++- wallet/db_postgres_sqlgen.c | 2 +- wallet/db_sqlite3_sqlgen.c | 2 +- wallet/statements_gettextgen.po | 6 ++--- wallet/test/run-wallet.c | 3 ++- 14 files changed, 63 insertions(+), 24 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 5dcccc5669ca..7d649f8d0a03 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2460,7 +2460,8 @@ static bool capture_premature_msg(const u8 ***shit_lnd_says, const u8 *msg) } static void peer_reconnect(struct peer *peer, - const struct secret *last_remote_per_commit_secret) + const struct secret *last_remote_per_commit_secret, + u8 *reestablish_only) { struct channel_id channel_id; /* Note: BOLT #2 uses these names! */ @@ -2541,6 +2542,13 @@ static void peer_reconnect(struct peer *peer, bool soft_error = peer->funding_locked[REMOTE] || peer->funding_locked[LOCAL]; + /* If they sent reestablish, we analyze it for courtesy, but also + * in case *they* are ahead of us! */ + if (reestablish_only) { + msg = reestablish_only; + goto got_reestablish; + } + /* Read until they say something interesting (don't forward * gossip *to* them yet: we might try sending channel_update * before we've reestablished channel). */ @@ -2552,6 +2560,7 @@ static void peer_reconnect(struct peer *peer, msg) || capture_premature_msg(&premature_msgs, msg)); +got_reestablish: if (!fromwire_channel_reestablish(msg, &channel_id, &next_commitment_number, @@ -2734,6 +2743,12 @@ static void peer_reconnect(struct peer *peer, /* (If we had sent `closing_signed`, we'd be in closingd). */ maybe_send_shutdown(peer); + /* Now stop, we've been polite long enough. */ + if (reestablish_only) + peer_failed_err(peer->pps, + &peer->channel_id, + "Channel is already closed"); + /* Corner case: we didn't send shutdown before because update_add_htlc * pending, but now they're cleared by restart, and we're actually * complete. In that case, their `shutdown` will trigger us. */ @@ -3223,6 +3238,7 @@ static void init_channel(struct peer *peer) secp256k1_ecdsa_signature *remote_ann_bitcoin_sig; bool option_static_remotekey, option_anchor_outputs; struct penalty_base *pbases; + u8 *reestablish_only; #if !DEVELOPER bool dev_fail_process_onionpacket; /* Ignored */ #endif @@ -3284,7 +3300,8 @@ static void init_channel(struct peer *peer) &option_anchor_outputs, &dev_fast_gossip, &dev_fail_process_onionpacket, - &pbases)) { + &pbases, + &reestablish_only)) { master_badmsg(WIRE_CHANNELD_INIT, msg); } @@ -3373,7 +3390,10 @@ static void init_channel(struct peer *peer) /* OK, now we can process peer messages. */ if (reconnected) - peer_reconnect(peer, &last_remote_per_commit_secret); + peer_reconnect(peer, &last_remote_per_commit_secret, + reestablish_only); + else + assert(!reestablish_only); /* If we have a messages to send, send them immediately */ if (fwd_msg) diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index a376f5a6536f..342af836fe3a 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -73,6 +73,8 @@ msgdata,channeld_init,dev_fast_gossip,bool, msgdata,channeld_init,dev_fail_process_onionpacket,bool, msgdata,channeld_init,num_penalty_bases,u32, msgdata,channeld_init,pbases,penalty_base,num_penalty_bases +msgdata,channeld_init,reestablish_only_len,u16, +msgdata,channeld_init,reestablish_only,u8,reestablish_only_len # master->channeld funding hit new depth(funding locked if >= lock depth) msgtype,channeld_funding_depth,1002 diff --git a/channeld/channeld_wiregen.c b/channeld/channeld_wiregen.c index 4d4fe6c676e4..0a07817bb8ef 100644 --- a/channeld/channeld_wiregen.c +++ b/channeld/channeld_wiregen.c @@ -96,7 +96,7 @@ bool channeld_wire_is_defined(u16 type) /* WIRE: CHANNELD_INIT */ /* Begin! (passes gossipd-client fd) */ -u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, u32 minimum_depth, const struct channel_config *our_config, const struct channel_config *their_config, const struct fee_states *fee_states, u32 feerate_min, u32 feerate_max, u32 feerate_penalty, const struct bitcoin_signature *first_commit_sig, const struct per_peer_state *per_peer_state, const struct pubkey *remote_fundingkey, const struct basepoints *remote_basepoints, const struct pubkey *remote_per_commit, const struct pubkey *old_remote_per_commit, enum side opener, u32 fee_base, u32 fee_proportional, struct amount_msat local_msatoshi, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct node_id *local_node_id, const struct node_id *remote_node_id, u32 commit_msec, u16 cltv_delta, bool last_was_revoke, const struct changed_htlc *last_sent_commit, u64 next_index_local, u64 next_index_remote, u64 revocations_received, u64 next_htlc_id, const struct existing_htlc **htlcs, bool local_funding_locked, bool remote_funding_locked, const struct short_channel_id *funding_short_id, bool reestablish, bool send_shutdown, bool remote_shutdown_received, const u8 *final_scriptpubkey, u8 flags, const u8 *init_peer_pkt, bool reached_announce_depth, const struct secret *last_remote_secret, const u8 *their_features, const u8 *upfront_shutdown_script, const secp256k1_ecdsa_signature *remote_ann_node_sig, const secp256k1_ecdsa_signature *remote_ann_bitcoin_sig, bool option_static_remotekey, bool option_anchor_outputs, bool dev_fast_gossip, bool dev_fail_process_onionpacket, const struct penalty_base *pbases) +u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, u32 minimum_depth, const struct channel_config *our_config, const struct channel_config *their_config, const struct fee_states *fee_states, u32 feerate_min, u32 feerate_max, u32 feerate_penalty, const struct bitcoin_signature *first_commit_sig, const struct per_peer_state *per_peer_state, const struct pubkey *remote_fundingkey, const struct basepoints *remote_basepoints, const struct pubkey *remote_per_commit, const struct pubkey *old_remote_per_commit, enum side opener, u32 fee_base, u32 fee_proportional, struct amount_msat local_msatoshi, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct node_id *local_node_id, const struct node_id *remote_node_id, u32 commit_msec, u16 cltv_delta, bool last_was_revoke, const struct changed_htlc *last_sent_commit, u64 next_index_local, u64 next_index_remote, u64 revocations_received, u64 next_htlc_id, const struct existing_htlc **htlcs, bool local_funding_locked, bool remote_funding_locked, const struct short_channel_id *funding_short_id, bool reestablish, bool send_shutdown, bool remote_shutdown_received, const u8 *final_scriptpubkey, u8 flags, const u8 *init_peer_pkt, bool reached_announce_depth, const struct secret *last_remote_secret, const u8 *their_features, const u8 *upfront_shutdown_script, const secp256k1_ecdsa_signature *remote_ann_node_sig, const secp256k1_ecdsa_signature *remote_ann_bitcoin_sig, bool option_static_remotekey, bool option_anchor_outputs, bool dev_fast_gossip, bool dev_fail_process_onionpacket, const struct penalty_base *pbases, const u8 *reestablish_only) { u16 num_last_sent_commit = tal_count(last_sent_commit); u16 num_existing_htlcs = tal_count(htlcs); @@ -105,6 +105,7 @@ u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams u16 flen = tal_count(their_features); u16 upfront_shutdown_script_len = tal_count(upfront_shutdown_script); u32 num_penalty_bases = tal_count(pbases); + u16 reestablish_only_len = tal_count(reestablish_only); u8 *p = tal_arr(ctx, u8, 0); towire_u16(&p, WIRE_CHANNELD_INIT); @@ -184,10 +185,12 @@ u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams towire_u32(&p, num_penalty_bases); for (size_t i = 0; i < num_penalty_bases; i++) towire_penalty_base(&p, pbases + i); + towire_u16(&p, reestablish_only_len); + towire_u8_array(&p, reestablish_only, reestablish_only_len); return memcheck(p, tal_count(p)); } -bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, u32 *minimum_depth, struct channel_config *our_config, struct channel_config *their_config, struct fee_states **fee_states, u32 *feerate_min, u32 *feerate_max, u32 *feerate_penalty, struct bitcoin_signature *first_commit_sig, struct per_peer_state **per_peer_state, struct pubkey *remote_fundingkey, struct basepoints *remote_basepoints, struct pubkey *remote_per_commit, struct pubkey *old_remote_per_commit, enum side *opener, u32 *fee_base, u32 *fee_proportional, struct amount_msat *local_msatoshi, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct node_id *local_node_id, struct node_id *remote_node_id, u32 *commit_msec, u16 *cltv_delta, bool *last_was_revoke, struct changed_htlc **last_sent_commit, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u64 *next_htlc_id, struct existing_htlc ***htlcs, bool *local_funding_locked, bool *remote_funding_locked, struct short_channel_id *funding_short_id, bool *reestablish, bool *send_shutdown, bool *remote_shutdown_received, u8 **final_scriptpubkey, u8 *flags, u8 **init_peer_pkt, bool *reached_announce_depth, struct secret *last_remote_secret, u8 **their_features, u8 **upfront_shutdown_script, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig, bool *option_static_remotekey, bool *option_anchor_outputs, bool *dev_fast_gossip, bool *dev_fail_process_onionpacket, struct penalty_base **pbases) +bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, u32 *minimum_depth, struct channel_config *our_config, struct channel_config *their_config, struct fee_states **fee_states, u32 *feerate_min, u32 *feerate_max, u32 *feerate_penalty, struct bitcoin_signature *first_commit_sig, struct per_peer_state **per_peer_state, struct pubkey *remote_fundingkey, struct basepoints *remote_basepoints, struct pubkey *remote_per_commit, struct pubkey *old_remote_per_commit, enum side *opener, u32 *fee_base, u32 *fee_proportional, struct amount_msat *local_msatoshi, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct node_id *local_node_id, struct node_id *remote_node_id, u32 *commit_msec, u16 *cltv_delta, bool *last_was_revoke, struct changed_htlc **last_sent_commit, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u64 *next_htlc_id, struct existing_htlc ***htlcs, bool *local_funding_locked, bool *remote_funding_locked, struct short_channel_id *funding_short_id, bool *reestablish, bool *send_shutdown, bool *remote_shutdown_received, u8 **final_scriptpubkey, u8 *flags, u8 **init_peer_pkt, bool *reached_announce_depth, struct secret *last_remote_secret, u8 **their_features, u8 **upfront_shutdown_script, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig, bool *option_static_remotekey, bool *option_anchor_outputs, bool *dev_fast_gossip, bool *dev_fail_process_onionpacket, struct penalty_base **pbases, u8 **reestablish_only) { u16 num_last_sent_commit; u16 num_existing_htlcs; @@ -196,6 +199,7 @@ bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainp u16 flen; u16 upfront_shutdown_script_len; u32 num_penalty_bases; + u16 reestablish_only_len; const u8 *cursor = p; size_t plen = tal_count(p); @@ -292,6 +296,10 @@ bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainp *pbases = num_penalty_bases ? tal_arr(ctx, struct penalty_base, num_penalty_bases) : NULL; for (size_t i = 0; i < num_penalty_bases; i++) fromwire_penalty_base(&cursor, &plen, *pbases + i); + reestablish_only_len = fromwire_u16(&cursor, &plen); + // 2nd case reestablish_only + *reestablish_only = reestablish_only_len ? tal_arr(ctx, u8, reestablish_only_len) : NULL; + fromwire_u8_array(&cursor, &plen, *reestablish_only, reestablish_only_len); return cursor != NULL; } @@ -1113,4 +1121,4 @@ bool fromwire_channeld_dev_quiesce_reply(const void *p) return false; return cursor != NULL; } -// SHA256STAMP:720f9917311384d373593dc1550619ddf461bdabde8b312ed6dc632cb7860c34 +// SHA256STAMP:10899e0e2ea3867c0420dd4c6d46d9079ca14edd26077a3b1a7d5643e4b24ae1 diff --git a/channeld/channeld_wiregen.h b/channeld/channeld_wiregen.h index 7d6f16c5426d..cc26556bb33e 100644 --- a/channeld/channeld_wiregen.h +++ b/channeld/channeld_wiregen.h @@ -89,8 +89,8 @@ bool channeld_wire_is_defined(u16 type); /* WIRE: CHANNELD_INIT */ /* Begin! (passes gossipd-client fd) */ -u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, u32 minimum_depth, const struct channel_config *our_config, const struct channel_config *their_config, const struct fee_states *fee_states, u32 feerate_min, u32 feerate_max, u32 feerate_penalty, const struct bitcoin_signature *first_commit_sig, const struct per_peer_state *per_peer_state, const struct pubkey *remote_fundingkey, const struct basepoints *remote_basepoints, const struct pubkey *remote_per_commit, const struct pubkey *old_remote_per_commit, enum side opener, u32 fee_base, u32 fee_proportional, struct amount_msat local_msatoshi, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct node_id *local_node_id, const struct node_id *remote_node_id, u32 commit_msec, u16 cltv_delta, bool last_was_revoke, const struct changed_htlc *last_sent_commit, u64 next_index_local, u64 next_index_remote, u64 revocations_received, u64 next_htlc_id, const struct existing_htlc **htlcs, bool local_funding_locked, bool remote_funding_locked, const struct short_channel_id *funding_short_id, bool reestablish, bool send_shutdown, bool remote_shutdown_received, const u8 *final_scriptpubkey, u8 flags, const u8 *init_peer_pkt, bool reached_announce_depth, const struct secret *last_remote_secret, const u8 *their_features, const u8 *upfront_shutdown_script, const secp256k1_ecdsa_signature *remote_ann_node_sig, const secp256k1_ecdsa_signature *remote_ann_bitcoin_sig, bool option_static_remotekey, bool option_anchor_outputs, bool dev_fast_gossip, bool dev_fail_process_onionpacket, const struct penalty_base *pbases); -bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, u32 *minimum_depth, struct channel_config *our_config, struct channel_config *their_config, struct fee_states **fee_states, u32 *feerate_min, u32 *feerate_max, u32 *feerate_penalty, struct bitcoin_signature *first_commit_sig, struct per_peer_state **per_peer_state, struct pubkey *remote_fundingkey, struct basepoints *remote_basepoints, struct pubkey *remote_per_commit, struct pubkey *old_remote_per_commit, enum side *opener, u32 *fee_base, u32 *fee_proportional, struct amount_msat *local_msatoshi, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct node_id *local_node_id, struct node_id *remote_node_id, u32 *commit_msec, u16 *cltv_delta, bool *last_was_revoke, struct changed_htlc **last_sent_commit, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u64 *next_htlc_id, struct existing_htlc ***htlcs, bool *local_funding_locked, bool *remote_funding_locked, struct short_channel_id *funding_short_id, bool *reestablish, bool *send_shutdown, bool *remote_shutdown_received, u8 **final_scriptpubkey, u8 *flags, u8 **init_peer_pkt, bool *reached_announce_depth, struct secret *last_remote_secret, u8 **their_features, u8 **upfront_shutdown_script, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig, bool *option_static_remotekey, bool *option_anchor_outputs, bool *dev_fast_gossip, bool *dev_fail_process_onionpacket, struct penalty_base **pbases); +u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, u32 minimum_depth, const struct channel_config *our_config, const struct channel_config *their_config, const struct fee_states *fee_states, u32 feerate_min, u32 feerate_max, u32 feerate_penalty, const struct bitcoin_signature *first_commit_sig, const struct per_peer_state *per_peer_state, const struct pubkey *remote_fundingkey, const struct basepoints *remote_basepoints, const struct pubkey *remote_per_commit, const struct pubkey *old_remote_per_commit, enum side opener, u32 fee_base, u32 fee_proportional, struct amount_msat local_msatoshi, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct node_id *local_node_id, const struct node_id *remote_node_id, u32 commit_msec, u16 cltv_delta, bool last_was_revoke, const struct changed_htlc *last_sent_commit, u64 next_index_local, u64 next_index_remote, u64 revocations_received, u64 next_htlc_id, const struct existing_htlc **htlcs, bool local_funding_locked, bool remote_funding_locked, const struct short_channel_id *funding_short_id, bool reestablish, bool send_shutdown, bool remote_shutdown_received, const u8 *final_scriptpubkey, u8 flags, const u8 *init_peer_pkt, bool reached_announce_depth, const struct secret *last_remote_secret, const u8 *their_features, const u8 *upfront_shutdown_script, const secp256k1_ecdsa_signature *remote_ann_node_sig, const secp256k1_ecdsa_signature *remote_ann_bitcoin_sig, bool option_static_remotekey, bool option_anchor_outputs, bool dev_fast_gossip, bool dev_fail_process_onionpacket, const struct penalty_base *pbases, const u8 *reestablish_only); +bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, u32 *minimum_depth, struct channel_config *our_config, struct channel_config *their_config, struct fee_states **fee_states, u32 *feerate_min, u32 *feerate_max, u32 *feerate_penalty, struct bitcoin_signature *first_commit_sig, struct per_peer_state **per_peer_state, struct pubkey *remote_fundingkey, struct basepoints *remote_basepoints, struct pubkey *remote_per_commit, struct pubkey *old_remote_per_commit, enum side *opener, u32 *fee_base, u32 *fee_proportional, struct amount_msat *local_msatoshi, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct node_id *local_node_id, struct node_id *remote_node_id, u32 *commit_msec, u16 *cltv_delta, bool *last_was_revoke, struct changed_htlc **last_sent_commit, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u64 *next_htlc_id, struct existing_htlc ***htlcs, bool *local_funding_locked, bool *remote_funding_locked, struct short_channel_id *funding_short_id, bool *reestablish, bool *send_shutdown, bool *remote_shutdown_received, u8 **final_scriptpubkey, u8 *flags, u8 **init_peer_pkt, bool *reached_announce_depth, struct secret *last_remote_secret, u8 **their_features, u8 **upfront_shutdown_script, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig, bool *option_static_remotekey, bool *option_anchor_outputs, bool *dev_fast_gossip, bool *dev_fail_process_onionpacket, struct penalty_base **pbases, u8 **reestablish_only); /* WIRE: CHANNELD_FUNDING_DEPTH */ /* master->channeld funding hit new depth(funding locked if >= lock depth) */ @@ -225,4 +225,4 @@ bool fromwire_channeld_dev_quiesce_reply(const void *p); #endif /* LIGHTNING_CHANNELD_CHANNELD_WIREGEN_H */ -// SHA256STAMP:720f9917311384d373593dc1550619ddf461bdabde8b312ed6dc632cb7860c34 +// SHA256STAMP:10899e0e2ea3867c0420dd4c6d46d9079ca14edd26077a3b1a7d5643e4b24ae1 diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 79cd2b5ea709..03d0b65738b3 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -457,7 +457,8 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) void peer_start_channeld(struct channel *channel, struct per_peer_state *pps, const u8 *fwd_msg, - bool reconnected) + bool reconnected, + const u8 *reestablish_only) { u8 *initmsg; int hsmfd; @@ -598,8 +599,11 @@ void peer_start_channeld(struct channel *channel, channel->remote_funding_locked, &scid, reconnected, + /* Anything that indicates we are or have + * shut down */ channel->state == CHANNELD_SHUTTING_DOWN - || channel->state == CLOSINGD_SIGEXCHANGE, + || channel->state == CLOSINGD_SIGEXCHANGE + || channel_closed(channel), channel->shutdown_scriptpubkey[REMOTE] != NULL, channel->shutdown_scriptpubkey[LOCAL], channel->channel_flags, @@ -616,7 +620,8 @@ void peer_start_channeld(struct channel *channel, channel->option_anchor_outputs, IFDEV(ld->dev_fast_gossip, false), IFDEV(dev_fail_process_onionpacket, false), - pbases); + pbases, + reestablish_only); /* We don't expect a response: we are triggered by funding_depth_cb. */ subd_send_msg(channel->owner, take(initmsg)); diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index 5290a64a99af..bf32b5897ba4 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -13,7 +13,8 @@ struct peer; void peer_start_channeld(struct channel *channel, struct per_peer_state *pps, const u8 *fwd_msg, - bool reconnected); + bool reconnected, + const u8 *reestablish_only); /* Returns true if subd told, otherwise false. */ bool channel_tell_depth(struct lightningd *ld, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 83f178c24db7..39836fcfd844 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1547,7 +1547,7 @@ static void handle_channel_locked(struct subd *dualopend, wallet_channel_clear_inflights(dualopend->ld->wallet, channel); /* FIXME: LND sigs/update_fee msgs? */ - peer_start_channeld(channel, pps, NULL, false); + peer_start_channeld(channel, pps, NULL, false, NULL); return; } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 684c472b154e..95bdf0dd8569 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -417,7 +417,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, wallet_penalty_base_add(ld->wallet, channel->dbid, pbase); funding_success(channel); - peer_start_channeld(channel, pps, NULL, false); + peer_start_channeld(channel, pps, NULL, false, NULL); cleanup: subd_release_channel(openingd, fc->uc); @@ -532,7 +532,7 @@ static void opening_fundee_finished(struct subd *openingd, wallet_penalty_base_add(ld->wallet, channel->dbid, pbase); /* On to normal operation! */ - peer_start_channeld(channel, pps, fwd_msg, false); + peer_start_channeld(channel, pps, fwd_msg, false, NULL); subd_release_channel(openingd, uc); uc->open_daemon = NULL; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 5b019668e756..8f44933b6752 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1122,7 +1122,8 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa assert(!channel->owner); channel->peer->addr = addr; channel->peer->connected_incoming = payload->incoming; - peer_start_channeld(channel, payload->pps, NULL, true); + peer_start_channeld(channel, payload->pps, NULL, true, + NULL); return; } abort(); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index c9fc3d5355fb..a9d6b163eeac 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -575,7 +575,8 @@ void peer_restart_dualopend(struct peer *peer UNNEEDED, void peer_start_channeld(struct channel *channel UNNEEDED, struct per_peer_state *pps UNNEEDED, const u8 *fwd_msg UNNEEDED, - bool reconnected UNNEEDED) + bool reconnected UNNEEDED, + const u8 *reestablish_only UNNEEDED) { fprintf(stderr, "peer_start_channeld called!\n"); abort(); } /* Generated stub for peer_start_dualopend */ void peer_start_dualopend(struct peer *peer UNNEEDED, struct per_peer_state *pps UNNEEDED) diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index 0532e5341813..734d05df74d0 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_postgres_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:44990f6dd9d83577ced85310f3b32ae7bb2dd2d21a4fdc08ac65596f23012aaa +// SHA256STAMP:e70eb362137c650b690227b27f7731fbe23a23694f308db76e28c96517ea02e9 diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index aa0127355682..acb2944f9386 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -1906,4 +1906,4 @@ struct db_query db_sqlite3_queries[] = { #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:44990f6dd9d83577ced85310f3b32ae7bb2dd2d21a4fdc08ac65596f23012aaa +// SHA256STAMP:e70eb362137c650b690227b27f7731fbe23a23694f308db76e28c96517ea02e9 diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index 68e6b4381381..6c8f587573a9 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1250,11 +1250,11 @@ msgstr "" msgid "not a valid SQL statement" msgstr "" -#: wallet/test/run-wallet.c:1445 +#: wallet/test/run-wallet.c:1446 msgid "SELECT COUNT(1) FROM channel_funding_inflights WHERE channel_id = ?;" msgstr "" -#: wallet/test/run-wallet.c:1643 +#: wallet/test/run-wallet.c:1644 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:57b906c6eff2852af492ca63454e88e65d9350017c9458f9f31bd7c2665cd8a8 +# SHA256STAMP:36fab85678bf55501b0d4e50be7f28b760160d4f271c08c4fe4c8d33117056f1 diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index df63d6f12289..62f6180ec756 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -647,7 +647,8 @@ void peer_restart_dualopend(struct peer *peer UNNEEDED, void peer_start_channeld(struct channel *channel UNNEEDED, struct per_peer_state *pps UNNEEDED, const u8 *fwd_msg UNNEEDED, - bool reconnected UNNEEDED) + bool reconnected UNNEEDED, + const u8 *reestablish_only UNNEEDED) { fprintf(stderr, "peer_start_channeld called!\n"); abort(); } /* Generated stub for peer_start_dualopend */ void peer_start_dualopend(struct peer *peer UNNEEDED, struct per_peer_state *pps UNNEEDED) From 7ed1aea15a05a7787da6b796b9086caf4a4bbee1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 14:49:34 +0930 Subject: [PATCH 60/62] openingd: tell lightningd if we get a reestablish. --- lightningd/opening_common.c | 32 +++++++++++++++++++++++++++++++ lightningd/opening_common.h | 6 ++++++ lightningd/opening_control.c | 31 ++++++++++++++++++++++++++++++ openingd/openingd.c | 13 ++++++++++++- openingd/openingd_wire.csv | 7 +++++++ openingd/openingd_wiregen.c | 37 +++++++++++++++++++++++++++++++++++- openingd/openingd_wiregen.h | 9 ++++++++- 7 files changed, 132 insertions(+), 3 deletions(-) diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 4f50d4b37250..d1d288078d08 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -1,8 +1,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -151,6 +153,36 @@ void channel_config(struct lightningd *ld, ours->channel_reserve = AMOUNT_SAT(UINT64_MAX); } +void handle_reestablish(struct lightningd *ld, + const struct node_id *peer_id, + const struct channel_id *channel_id, + const u8 *reestablish, + struct per_peer_state *pps) +{ + struct peer *peer; + struct channel *c; + + /* We very carefully re-xmit the last reestablish, so they can get + * their secrets back. We don't otherwise touch them. */ + peer = peer_by_id(ld, peer_id); + if (peer) + c = find_channel_by_id(peer, channel_id); + else + c = NULL; + + if (c && channel_closed(c)) { + log_debug(c->log, "Reestablish on %s channel: using channeld to reply", + channel_state_name(c)); + peer_start_channeld(c, pps, NULL, true, reestablish); + } else { + const u8 *err = towire_errorfmt(NULL, channel_id, + "Unknown channel for reestablish"); + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, peer_id, + pps, err))); + } +} + #if DEVELOPER /* Indented to avoid include ordering check */ #include diff --git a/lightningd/opening_common.h b/lightningd/opening_common.h index 212a9109c13a..b499407e6437 100644 --- a/lightningd/opening_common.h +++ b/lightningd/opening_common.h @@ -118,6 +118,12 @@ void channel_config(struct lightningd *ld, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity); +void handle_reestablish(struct lightningd *ld, + const struct node_id *peer_id, + const struct channel_id *channel_id, + const u8 *reestablish, + struct per_peer_state *pps); + #if DEVELOPER struct command; /* Calls report_leak_info() async. */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 95bdf0dd8569..fe7bf1b84424 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -798,6 +798,31 @@ static void opening_got_offer(struct subd *openingd, plugin_hook_call_openchannel(openingd->ld, payload); } +static void opening_got_reestablish(struct subd *openingd, const u8 *msg, + const int fds[3], + struct uncommitted_channel *uc) +{ + struct lightningd *ld = openingd->ld; + struct node_id peer_id = uc->peer->id; + struct channel_id channel_id; + u8 *reestablish; + struct per_peer_state *pps; + + if (!fromwire_openingd_got_reestablish(tmpctx, msg, &channel_id, + &reestablish, &pps)) { + log_broken(openingd->log, "Malformed opening_got_reestablish %s", + tal_hex(tmpctx, msg)); + tal_free(openingd); + return; + } + per_peer_state_set_fds_arr(pps, fds); + + /* This could free peer */ + tal_free(uc); + + handle_reestablish(ld, &peer_id, &channel_id, reestablish, pps); +} + static unsigned int openingd_msg(struct subd *openingd, const u8 *msg, const int *fds) { @@ -845,6 +870,12 @@ static unsigned int openingd_msg(struct subd *openingd, opening_got_offer(openingd, msg, uc); return 0; + case WIRE_OPENINGD_GOT_REESTABLISH: + if (tal_count(fds) != 3) + return 3; + opening_got_reestablish(openingd, msg, fds, uc); + return 0; + /* We send these! */ case WIRE_OPENINGD_INIT: case WIRE_OPENINGD_FUNDER_START: diff --git a/openingd/openingd.c b/openingd/openingd.c index 7a0d1bdeed2d..43ad981ab858 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1149,6 +1149,7 @@ static u8 *handle_peer_in(struct state *state) u8 *msg = sync_crypto_read(tmpctx, state->pps); enum peer_wire t = fromwire_peektype(msg); struct channel_id channel_id; + bool extracted; if (t == WIRE_OPEN_CHANNEL) return fundee_channel(state, msg); @@ -1170,9 +1171,18 @@ static u8 *handle_peer_in(struct state *state) &state->channel_id, false, msg)) return NULL; + extracted = extract_channel_id(msg, &channel_id); + + /* Reestablish on some now-closed channel? Be nice. */ + if (extracted && fromwire_peektype(msg) == WIRE_CHANNEL_REESTABLISH) { + return towire_openingd_got_reestablish(NULL, + &channel_id, msg, + state->pps); + } + sync_crypto_write(state->pps, take(towire_warningfmt(NULL, - extract_channel_id(msg, &channel_id) ? &channel_id : NULL, + extracted ? &channel_id : NULL, "Unexpected message %s: %s", peer_wire_name(t), tal_hex(tmpctx, msg)))); @@ -1287,6 +1297,7 @@ static u8 *handle_master_in(struct state *state) case WIRE_OPENINGD_FUNDER_FAILED: case WIRE_OPENINGD_GOT_OFFER: case WIRE_OPENINGD_GOT_OFFER_REPLY: + case WIRE_OPENINGD_GOT_REESTABLISH: break; } diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index 0c4e6ff80b4c..84f51bccfbc6 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -29,6 +29,13 @@ msgdata,openingd_init,option_anchor_outputs,bool, msgdata,openingd_init,dev_temporary_channel_id,?byte,32 msgdata,openingd_init,dev_fast_gossip,bool, +# Openingd->master: they tried to reestablish a channel. +msgtype,openingd_got_reestablish,6001 +msgdata,openingd_got_reestablish,channel_id,channel_id, +msgdata,openingd_got_reestablish,len,u16, +msgdata,openingd_got_reestablish,msg,u8,len +msgdata,openingd_got_reestablish,pps,per_peer_state, + # Openingd->master: they offered channel, should we continue? msgtype,openingd_got_offer,6005 msgdata,openingd_got_offer,funding_satoshis,amount_sat, diff --git a/openingd/openingd_wiregen.c b/openingd/openingd_wiregen.c index 28442fda3cea..fd95b855100d 100644 --- a/openingd/openingd_wiregen.c +++ b/openingd/openingd_wiregen.c @@ -21,6 +21,7 @@ const char *openingd_wire_name(int e) switch ((enum openingd_wire)e) { case WIRE_OPENINGD_INIT: return "WIRE_OPENINGD_INIT"; + case WIRE_OPENINGD_GOT_REESTABLISH: return "WIRE_OPENINGD_GOT_REESTABLISH"; case WIRE_OPENINGD_GOT_OFFER: return "WIRE_OPENINGD_GOT_OFFER"; case WIRE_OPENINGD_GOT_OFFER_REPLY: return "WIRE_OPENINGD_GOT_OFFER_REPLY"; case WIRE_OPENINGD_FUNDER_REPLY: return "WIRE_OPENINGD_FUNDER_REPLY"; @@ -42,6 +43,7 @@ bool openingd_wire_is_defined(u16 type) { switch ((enum openingd_wire)type) { case WIRE_OPENINGD_INIT:; + case WIRE_OPENINGD_GOT_REESTABLISH:; case WIRE_OPENINGD_GOT_OFFER:; case WIRE_OPENINGD_GOT_OFFER_REPLY:; case WIRE_OPENINGD_FUNDER_REPLY:; @@ -138,6 +140,39 @@ bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainp return cursor != NULL; } +/* WIRE: OPENINGD_GOT_REESTABLISH */ +/* Openingd->master: they tried to reestablish a channel. */ +u8 *towire_openingd_got_reestablish(const tal_t *ctx, const struct channel_id *channel_id, const u8 *msg, const struct per_peer_state *pps) +{ + u16 len = tal_count(msg); + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_OPENINGD_GOT_REESTABLISH); + towire_channel_id(&p, channel_id); + towire_u16(&p, len); + towire_u8_array(&p, msg, len); + towire_per_peer_state(&p, pps); + + return memcheck(p, tal_count(p)); +} +bool fromwire_openingd_got_reestablish(const tal_t *ctx, const void *p, struct channel_id *channel_id, u8 **msg, struct per_peer_state **pps) +{ + u16 len; + + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_OPENINGD_GOT_REESTABLISH) + return false; + fromwire_channel_id(&cursor, &plen, channel_id); + len = fromwire_u16(&cursor, &plen); + // 2nd case msg + *msg = len ? tal_arr(ctx, u8, len) : NULL; + fromwire_u8_array(&cursor, &plen, *msg, len); + *pps = fromwire_per_peer_state(ctx, &cursor, &plen); + return cursor != NULL; +} + /* WIRE: OPENINGD_GOT_OFFER */ /* Openingd->master: they offered channel */ u8 *towire_openingd_got_offer(const tal_t *ctx, struct amount_sat funding_satoshis, struct amount_msat push_msat, struct amount_sat dust_limit_satoshis, struct amount_msat max_htlc_value_in_flight_msat, struct amount_sat channel_reserve_satoshis, struct amount_msat htlc_minimum_msat, u32 feerate_per_kw, u16 to_self_delay, u16 max_accepted_htlcs, u8 channel_flags, const u8 *shutdown_scriptpubkey) @@ -569,4 +604,4 @@ bool fromwire_openingd_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:edd7ee392dff0ddd0dff3a383692ba852a403e64e43290dba5dece69ae438e61 +// SHA256STAMP:51b93d8202630dc83aaf453c6b3339a12b1464bd2fbbff650be035155b85a7f2 diff --git a/openingd/openingd_wiregen.h b/openingd/openingd_wiregen.h index ba64e5567272..714d331b568e 100644 --- a/openingd/openingd_wiregen.h +++ b/openingd/openingd_wiregen.h @@ -18,6 +18,8 @@ enum openingd_wire { WIRE_OPENINGD_INIT = 6000, + /* Openingd->master: they tried to reestablish a channel. */ + WIRE_OPENINGD_GOT_REESTABLISH = 6001, /* Openingd->master: they offered channel */ WIRE_OPENINGD_GOT_OFFER = 6005, /* master->openingd: optional rejection message */ @@ -61,6 +63,11 @@ bool openingd_wire_is_defined(u16 type); u8 *towire_openingd_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_config *our_config, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, u32 minimum_depth, u32 min_feerate, u32 max_feerate, const u8 *lfeatures, bool option_static_remotekey, bool option_anchor_outputs, const struct channel_id *dev_temporary_channel_id, bool dev_fast_gossip); bool fromwire_openingd_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_config *our_config, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, u32 *minimum_depth, u32 *min_feerate, u32 *max_feerate, u8 **lfeatures, bool *option_static_remotekey, bool *option_anchor_outputs, struct channel_id **dev_temporary_channel_id, bool *dev_fast_gossip); +/* WIRE: OPENINGD_GOT_REESTABLISH */ +/* Openingd->master: they tried to reestablish a channel. */ +u8 *towire_openingd_got_reestablish(const tal_t *ctx, const struct channel_id *channel_id, const u8 *msg, const struct per_peer_state *pps); +bool fromwire_openingd_got_reestablish(const tal_t *ctx, const void *p, struct channel_id *channel_id, u8 **msg, struct per_peer_state **pps); + /* WIRE: OPENINGD_GOT_OFFER */ /* Openingd->master: they offered channel */ u8 *towire_openingd_got_offer(const tal_t *ctx, struct amount_sat funding_satoshis, struct amount_msat push_msat, struct amount_sat dust_limit_satoshis, struct amount_msat max_htlc_value_in_flight_msat, struct amount_sat channel_reserve_satoshis, struct amount_msat htlc_minimum_msat, u32 feerate_per_kw, u16 to_self_delay, u16 max_accepted_htlcs, u8 channel_flags, const u8 *shutdown_scriptpubkey); @@ -121,4 +128,4 @@ bool fromwire_openingd_dev_memleak_reply(const void *p, bool *leak); #endif /* LIGHTNING_OPENINGD_OPENINGD_WIREGEN_H */ -// SHA256STAMP:edd7ee392dff0ddd0dff3a383692ba852a403e64e43290dba5dece69ae438e61 +// SHA256STAMP:51b93d8202630dc83aaf453c6b3339a12b1464bd2fbbff650be035155b85a7f2 From cf328afcc5d0c0872c422f7cd13f02e644fe6457 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 14:49:34 +0930 Subject: [PATCH 61/62] channeld: send shutdown_complete even if reestablish_only. This lets us transition (with a few supporting changes) to closingd, which will happily let them mutual close with us. We already handle the case where this mutual close is redundant (for packet loss), so this is easy. Signed-off-by: Rusty Russell Changelog-Added: Protocol: We will now reestablish and negotiate mutual close on channels we've already closed (great if peer has lost their database). --- channeld/channeld.c | 27 +++++++++++++++++---------- lightningd/channel_control.c | 3 ++- lightningd/closing_control.c | 2 +- tests/test_closing.py | 1 - 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 7d649f8d0a03..cd16a132da33 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -874,6 +874,15 @@ static void maybe_send_shutdown(struct peer *peer) billboard_update(peer); } +static void send_shutdown_complete(struct peer *peer) +{ + /* Now we can tell master shutdown is complete. */ + wire_sync_write(MASTER_FD, + take(towire_channeld_shutdown_complete(NULL, peer->pps))); + per_peer_state_fdpass_send(MASTER_FD, peer->pps); + close(MASTER_FD); +} + /* This queues other traffic from the fd until we get reply. */ static u8 *master_wait_sync_reply(const tal_t *ctx, struct peer *peer, @@ -2744,10 +2753,17 @@ static void peer_reconnect(struct peer *peer, maybe_send_shutdown(peer); /* Now stop, we've been polite long enough. */ - if (reestablish_only) + if (reestablish_only) { + /* If we were successfully closing, we still go to closingd. */ + if (shutdown_complete(peer)) { + send_shutdown_complete(peer); + daemon_shutdown(); + exit(0); + } peer_failed_err(peer->pps, &peer->channel_id, "Channel is already closed"); + } /* Corner case: we didn't send shutdown before because update_add_htlc * pending, but now they're cleared by restart, and we're actually @@ -3405,15 +3421,6 @@ static void init_channel(struct peer *peer) billboard_update(peer); } -static void send_shutdown_complete(struct peer *peer) -{ - /* Now we can tell master shutdown is complete. */ - wire_sync_write(MASTER_FD, - take(towire_channeld_shutdown_complete(NULL, peer->pps))); - per_peer_state_fdpass_send(MASTER_FD, peer->pps); - close(MASTER_FD); -} - static void try_read_gossip_store(struct peer *peer) { u8 *msg = gossip_store_next(tmpctx, peer->pps); diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 03d0b65738b3..9abaf7ef9713 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -324,7 +324,8 @@ static void peer_start_closingd_after_shutdown(struct channel *channel, peer_start_closingd(channel, pps); /* We might have reconnected, so already be here. */ - if (channel->state != CLOSINGD_SIGEXCHANGE) + if (!channel_closed(channel) + && channel->state != CLOSINGD_SIGEXCHANGE) channel_set_state(channel, CHANNELD_SHUTTING_DOWN, CLOSINGD_SIGEXCHANGE, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 0729ee76d3b4..d2070875715f 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -145,7 +145,7 @@ static void peer_closing_complete(struct channel *channel, const u8 *msg) channel_set_billboard(channel, false, NULL); /* Retransmission only, ignore closing. */ - if (channel->state == CLOSINGD_COMPLETE) + if (channel_closed(channel)) return; /* Channel gets dropped to chain cooperatively. */ diff --git a/tests/test_closing.py b/tests/test_closing.py index 2219c82ada2a..182a0d8ff834 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2786,7 +2786,6 @@ def test_htlc_rexmit_while_closing(node_factory, executor): fut2.result(TIMEOUT) -@pytest.mark.xfail(strict=True) @pytest.mark.openchannel('v1') @pytest.mark.developer("needs dev_disconnect") def test_you_forgot_closed_channel(node_factory, executor): From 38fb12cc9fdb0f2570be67a0aa40f78434f7f421 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Jun 2021 14:49:34 +0930 Subject: [PATCH 62/62] pytest: Add test that we can redo closing negotiation even after onchain. Signed-off-by: Rusty Russell --- tests/test_closing.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 182a0d8ff834..61ef20087a99 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2789,7 +2789,7 @@ def test_htlc_rexmit_while_closing(node_factory, executor): @pytest.mark.openchannel('v1') @pytest.mark.developer("needs dev_disconnect") def test_you_forgot_closed_channel(node_factory, executor): - """Ideally you'd keep talking to us about closed channels""" + """Ideally you'd keep talking to us about closed channels: simple""" disconnects = ['@WIRE_CLOSING_SIGNED'] l1, l2 = node_factory.line_graph(2, opts=[{'may_reconnect': True, @@ -2809,3 +2809,38 @@ def test_you_forgot_closed_channel(node_factory, executor): # l1 reconnects, it should succeed. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) fut.result(TIMEOUT) + + +@pytest.mark.developer("needs dev_disconnect") +def test_you_forgot_closed_channel_onchain(node_factory, bitcoind, executor): + """Ideally you'd keep talking to us about closed channels: even if close is mined""" + disconnects = ['@WIRE_CLOSING_SIGNED'] + + l1, l2 = node_factory.line_graph(2, opts=[{'may_reconnect': True, + 'dev-no-reconnect': None}, + {'may_reconnect': True, + 'dev-no-reconnect': None, + 'disconnect': disconnects}]) + + l1.pay(l2, 200000) + + fut = executor.submit(l1.rpc.close, l2.info['id']) + + # l2 considers the closing done, l1 does not + wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_COMPLETE') + assert only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE' + + # l1 does not see any new blocks. + def no_new_blocks(req): + return {"result": {"blockhash": None, "block": None}} + + l1.daemon.rpcproxy.mock_rpc('getrawblockbyheight', no_new_blocks) + + # Close transaction mined + bitcoind.generate_block(1, wait_for_mempool=1) + + wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'ONCHAIN') + + # l1 reconnects, it should succeed. + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + fut.result(TIMEOUT)